map类型

Go语言中的map类型是一种key-value(键值对)结构,用于存储一组无序的数据,其中键是唯一的,值可以相同或许不同。在Go语言的程序开发中,map运用广泛,尤其是用于缓存、关联数据等场景下。

声明和初始化

声明和初始化map的办法有三种:

(1)声明变量后运用make函数创建map

var m map[string]int  // 声明一个map变量m
m = make(map[string]int)  // 运用make函数创建一个map对象

(2)运用字面量办法创建map

m := map[string]int{
    "apple": 1, 
    "banana": 2,
}

(3)运用make函数创建map并一起初始化

m := make(map[string]int, 10)  // 初始化容量为10的map

元素操作

map中的元素是键值对(key-value)的形式存储的,其中键有必要是支持==比较运算符的类型,如string、数字类型、char等;值可所以恣意类型。map中常用的元素操作有:

(1)增加或修正元素

m["peach"] = 3  // 增加单个元素
m["banana"] = 4  // 修正单个元素

(2)删去元素

delete(m, "banana")  // 删去单个元素

(3)获取元素及判别元素是否存在

v, ok := m["peach"]  // 获取单个元素
if ok {
    fmt.Println(v)
}
// 或许运用下面的办法判别元素是否存在
if _, ok := m["banana"]; ok {
    fmt.Println("banana is exist")
}

(4)遍历map中的所有元素

for k, v := range m {
    fmt.Printf("%s -> %d\n", k, v)
}

(5)清空map

for k := range m {
    delete(m, k)
}

巨细和容量

map的长度(长度表明map元素的个数),可以通过len()函数取得

length := len(m)

map的容量(容量表明map底层的哈希表容量),不能改动,只能通过make函数初始化时设置容量

m := make(map[string]int, 16) // 初始化容量为16的map

并发问题

map不是线程安全的,多个goroutine一起对同一个map进行读写操作,会发生竞态条件,导致程序运转异常。

处理map的并发问题,可以运用sync包提供的锁机制进行同步,例如:

import "sync"
var mu sync.Mutex  // 创建一个互斥锁
// goroutine1
mu.Lock()  // 上锁
m["apple"] = 1
mu.Unlock()  // 解锁
// goroutine2
mu.Lock()  // 上锁
v, _ := m["apple"]
fmt.Println(v)
mu.Unlock()  // 解锁

完成原理

map底层是根据哈希表完成的,map运用哈希表来存储键值对,通过哈希函数将键映射为哈希值,用哈希值作为数组的下标,将值储存在以哈希值为下标的数组槽中。但是,哈希函数并不是绝对牢靠的,假如两个不同的键映射为同一个哈希值,这个情况被称为哈希抵触。

处理哈希抵触一般有以下两种办法:

(1)链表法

将哈希值相同的键值对附加到同一个链表上,这个链表通常被称为buckets。当键值对数量较少时,链表法是一种非常有效的处理哈希抵触的办法。

(2)敞开地址法

敞开地址法有三种常见的完成办法:线性探测法、二次探测法和双重散列法。敞开地址法的首要思维是:当出现抵触时,次序,直到找到一个闲暇的槽位停止。

留意

  • Map的元素遍历次序:Map的元素遍历次序是随机的,不确保元素的次序

  • Map的key类型:Map的key可所以恣意可比较类型,如整数、浮点数、字符串、数组、结构体等,但不可所以切片、函数、Map等不可比较类型

  • Map的value类型:Map的value可所以恣意类型,包含切片、函数、Map等杂乱类型。

  • Map的值传递:Map在函数参数传递时是按引用传递的,即修正传入的Map会对原Map发生影响。