go map相关 | 青训营笔记

156 阅读3分钟

这是我参与「第五届青训营」笔记创作活动的第十四天

map相关

从网上找到了一些关于map的题目,按照自己的话进行了一下整理

1、map 使用注意的点,是否并发安全?

map 的key类型一定是要可以比较的,通常情况下选择内建的数据结构,如果想要用结构体作为key类型,那么这个结构体对象最好是不变的。map是无序的,如果我们想要有一个有序的map,我们可以自己封装一个map,像java的LinkedHashMap,底层用一个双向链表来维护顺序。

map是本身是有并发安全问题的。

2、map 循环是有序的还是无序的?

map的循环是无序的,因为map这种数据结构本身是由hash表实现的,hash表本身就不具备有序这个特征,并且range map在循环逻辑的时候还做了随机播种,导致每一次输出都是不一样的

  m2 := make(map[int]int, 0)
  m2[1] = 1
  m2[2] = 2
  m2[3] = 3
  m2[4] = 4
  for _, v := range m2 {
    fmt.Println(v)
  }
  //打印结果
  //2 4
  //3 1
  //4 2
  //1 3
3、 map 中删除一个 key,它的内存会释放么?(常问)

map的底层是有一个hash表的,这个hash表本身就占用内存。

如果这个key对应的value是一个值类型,那么内存不会释放。如果这个key对应的value是一个引用类型,那么会释放这个元素对内存空间的占用。

4、怎么处理对 map 进行并发访问?有没有其他方案? 区别是什么?
  1. 使用读写锁:

    通过使用sync.Mutex锁或sync.RWMutex读写锁来保证同一时刻只有一个goroutine对map进行读写。例如:

    import (
      "sync"
    )
    ​
    var m sync.RWMutex
    var data = make(map[string]int)
    ​
    func readData(key string) int {
      m.RLock()
      defer m.RUnlock()
      return data[key]
    }
    ​
    func writeData(key string, value int) {
      m.Lock()
      defer m.Unlock()
      data[key] = value
    }
    ​
    
  2. 使用内置的sync.Map

    sync.Map是一个并发安全的map实现,与普通map不同,它不需要加锁就可以进行读写操作,因此可以减少死锁的风险。

    使用方法如下:

    1. 创建一个sync.Map对象:
    goCopy code
    m := sync.Map{}
    
    1. 设置键值对:
    goCopy code
    m.Store("key", "value")
    
    1. 获取键的值:
    goCopy code
    val, ok := m.Load("key")
    if ok {
        // Key is present
    } else {
        // Key is absent
    }
    
    1. 删除键值对:
    goCopy code
    m.Delete("key")
    
    1. 遍历键值对:
    goCopy code
    m.Range(func(key, value interface{}) bool {
        // Do something with key and value
        return true
    })
    

    这是使用sync.Map的简单示例。它是并发安全的,因此可以支持多个协程的并发访问。

5、 nil map 和空 map 有何不同?

nil map是这个对象还没有在内存中分配,还不存在,因此还不能被操作;空map是这个对象已经在内存当中分配并初始化,只是还没有使用

6、map 的数据结构是什么?是怎么实现扩容?

go map的本质也是使用hash table + 链表的方式实现。

map的基本数据结构用的是hmap + 桶实现

7、slices能作为map类型的key吗?

能够被比较的类型可以作为map类型的key,slice、map、functions都不可以作为key