Go第七课-复合类型:map

92 阅读3分钟

Map声明

  1. map 类型是一个无序的键值对的集合。

    map[KeyType]ValueType
    

显式赋值

  1. 使用复合字面值,Go 允许省略字面值中的元素类型

    m1 := map[int][]string{
    		1: []string{"val1_1", "val1_2"},
    		3: []string{"val3_1", "val3_2", "val3_3"},
    		7: []string{"val7_1"},
    	}
    
  2. 使用 make 这个预声明的内置函数。

    m1 := make(map[int]string) // 未指定初始容量
    m2 := make(map[int]string, 8) // 指定初始容量为8
    
  3. 没有显式地赋予 map 变量初值,map 类型变量的默认值为 nil。

  4. 初值为零值 nil 的切片类型变量,可以借助内置的 append 的函数进行操作,这种在 Go 语言中被称为“零值可用”,但 map 类型,因为它内部实现的复杂性,无法“零值可用”。

  5. 在访问的 Key 不存在时,仍会返回零值,不能通过返回 nil 来判断元素是否存在

    func TestMap(t *testing.T) {
    	m := map[string]int{"one": 1, "two": 2, "three": 3}
    	fmt.Println(m)
    	for k, v := range m {
    		t.Log(k, v)
    	}
    	if v, ok := m["four"]; ok {
    		t.Log("four", v)
    	} else {
    		t.Log(v, ok) // 0 false
    	}
    }
    

操作

  1. 操作一:插入新键值对。

    m := make(map[int]string)
    m[1] = "value1"
    m[2] = "value2"
    m[3] = "value3"
    
  2. 操作二:获取键值对数量。

    m := map[string]int {
      "key1" : 1,
      "key2" : 2,
    }
    
    fmt.Println(len(m)) // 2
    m["key3"] = 3  
    fmt.Println(len(m)) // 3
    
  3. 操作三:查找和数据读取

    m := make(map[string]int)
    v, ok := m["key1"]
    if !ok {
        // "key1"不在map中
    }
    
    // "key1"在map中,v将被赋予"key1"键对应的value
    
  4. delete(map, key)函数是从 map 中删除键的唯一方法。即便传给 delete 的键在 map 中并不存在,delete 函数的执行也不会失败,更不会抛出运行时的异常。

    m := map[string]int {
      "key1" : 1,
      "key2" : 2,
    }
    
    fmt.Println(m) // map[key1:1 key2:2]
    delete(m, "key2") // 删除"key2"
    fmt.Println(m) // map[key1:1]
    
  5. 遍历 map 的键值对只有一种方法,那就是像对待切片那样通过 for range 语句对 map 数据进行遍历。程序逻辑千万不要依赖遍历 map 所得到的的元素次序。

    for k, v := range m { fmt.Printf("[%d, %d] ", k, v) }
    
  6. 和切片类型一样,map 也是引用类型。

map 的内部实现

  1. 运行时实现了 map 类型操作的所有功能,包括查找、插入、删除等。在编译阶段,Go 编译器会将 Go 语法层面的 map 操作,重写成运行时对应的函数调用。
  2. Go 运行时是利用 maptype 参数中的信息确定 key 的类型和大小的。
  3. map 不是线程安全的,不支持并发读写;
  4. 考虑到 map 可以自动扩容,map 中数据元素的 value 位置可能在这一过程中发生变化,所以 Go 不允许获取 map 中 value 的地址,这个约束是在编译期间就生效的。

map 与⼯厂模式

  1. map 的 value 可以是⼀个方法

    func TestMapFunc(t *testing.T) {
    	m := map[int]func(op int) int{}
    	m[1] = func(op int) int { return op * op }
    	t.Log(m[1](3)) // 9
    }
    
  2. 与 Go 的 Dock type 接⼝方式⼀起,可以⽅便的实现单⼀方法对象的工厂模式

    func TestSet(t *testing.T) {
    	mySet := map[int]bool{}
    	mySet[1] = true
    	t.Log(len(mySet)) // 1
    	delete(mySet, 1)
    	t.Log(len(mySet)) // 0
    }