go——字典

Go中字典类型是散列表(hash table)的一个实现,其官方称谓是map。
散列表是一个实现了关联数组的数据结构,关联数组是用于表示键值对的无序集合的一种抽象数据类型。
Go中称键值对为键-元素对,它把字典中的每个键都看作与其对应的元素的索引,这样的索引再同一个字典值中是唯一的。
下面的代码声明了一个字典类型的变量:

1
var ipSwitches = map[string]bool{}

变量ipSwitches的键类型为string,元素类型为bool。
map[string]bool{}表示了一个不包含任何元素的字典值。
与切片类型一样,字典类型是一个引用类型。也正因此,字典类型的零值是nil。
字典值的长度表示了其中的键-元素对的数量,其零值的长度总是0.
将其作为语言内置类型,从运行时层面进行优化,可获得更高效的性能。
作为无序键值对集合,字典要求key必须是支持相等运算符(==、!=)的数据类型。

字典是引用类型,使用make函数或初始化表达式来创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
 
import "fmt"
 
func main() {
    m := make(map[string]int) //make函数创建
    m["a"] = 1
    m["b"] = 2
 
    m2 := map[int]struct { //key的类型int,value的类型struct
        x int
    }{
        1: {x: 100},
        2: {x: 200},
    }
    fmt.Println(m, m2) //map[a:1 b:2] map[1:{100} 2:{200}]
}

 

字典的基本操作:增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
 
import "fmt"
 
func main() {
    m := map[string]int{
        "a": 1,
        "b": 2,
    }
 
    m["a"] = 10    //修改
    m["c"] = 30    //添加
    fmt.Println(m) //map[a:10 b:2 c:30]
 
    if v, ok := m["d"]; ok { //使用ok-idiom判断键是否存在
        fmt.Println(v)
    }
 
    delete(m, "b")      //删除一个键值对
    fmt.Println(m)      //map[a:10 b:2 c:30]
    fmt.Println(m["f"]) //0  访问不存在的键值对时返回0值
}

 

对字典进行迭代每次返回的键值次序都不相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main
 
import "fmt"
 
func main() {
    m := make(map[string]int)
 
    for i := 0; i < 7; i++ {
        m[string('a'+i)] = i
    }
 
    for i := 0; i < 4; i++ {
        for k, v := range m {
            fmt.Printf("%s:%d  ", k, v)
        }
        fmt.Println()
    }
}
 
/*
g:6  a:0  b:1  c:2  d:3  e:4  f:5
e:4  f:5  g:6  a:0  b:1  c:2  d:3
g:6  a:0  b:1  c:2  d:3  e:4  f:5
a:0  b:1  c:2  d:3  e:4  f:5  g:6
*/

 

函数len返回当前键值对的数量,cap不接受字典类型.
因内存访问安全和哈希算法等缘故,字典被设计成"not addressable",
因此不能直接修改value成员(结构或数组)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
 
import "fmt"
 
func main() {
    type user struct {
        name string
        age  int
    }
 
    m := map[int]user{
        1: {"kebi", 19},
    }
    fmt.Println(len(m)) //1
    fmt.Println(cap(m)) //invalid argument m (type map[int]user) for cap
    m[1].age += 1       // cannot assign to struct field m[1].age in map
}

 

正确的做法是返回整个value,待修改后再设置字典键值,或直接用指针类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main
 
import "fmt"
 
func main() {
    type user struct {
        name string
        age  int
    }
 
    m := map[int]user{
        1: {"kebi", 19},
    }
 
    u := m[1]  //取出整个value值
    u.age += 1 //现在操作的对象是结构体
    m[1] = u   //重新赋值
    fmt.Println(m)
 
    m2 := map[int]*user{ //value是指针类型
        1: &user{"Jack", 20},
    }
    m2[1].age++ //m2[1]返回的是指针,透过指针修改目标对象
    fmt.Println(m2[1])
}

 

不能对nil字典进行赋值操作,但却能读。

1
2
3
4
5
6
7
8
9
10
11
12
package main
 
import "fmt"
 
func main() {
    var m map[string]int //只进行声明的字典是nil字典
    fmt.Println(m["a"])  //0
    // m["a"] = 1          //panic: assignment to entry in nil map
 
    m2 := map[string]int{}
    fmt.Println(m == nil, m2 == nil) //true  false
}

 

在迭代期间删除或新增键值是安全的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main
 
import "fmt"
 
func main() {
    m := make(map[int]int)
 
    for i := 0; i < 10; i++ {
        m[i] = i + 10
    }
 
    for k := range m {
        if k == 5 {
            m[100] = 1000
        }
        delete(m, k)
        fmt.Println(k, m)
    }
}
 
/*
0 map[9:19 2:12 6:16 8:18 7:17 1:11 3:13 4:14 5:15]
2 map[5:15 7:17 1:11 3:13 4:14 8:18 9:19 6:16]
6 map[4:14 5:15 7:17 1:11 3:13 8:18 9:19]
8 map[7:17 1:11 3:13 4:14 5:15 9:19]
9 map[4:14 5:15 7:17 1:11 3:13]
1 map[3:13 4:14 5:15 7:17]
3 map[4:14 5:15 7:17]
4 map[5:15 7:17]
5 map[7:17 100:1000]
7 map[100:1000]
*/

 

运行时会对字典并发操作做出检测.如果某个任务正在对字典进行写操作,
那么其它任务就不能对该字典执行并发操作(读写删除),否则会导致进程崩溃.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main
 
import "time"
 
func main() {
    m := make(map[string]int)
 
    go func() {
        for {
            m["a"] += 1
            time.Sleep(time.Microsecond)
        }
    }()
 
    go func() {
        for {
            _ = m["b"]
            time.Sleep(time.Microsecond)
        }
    }()
    select {}
}
 
 
//fatal error: concurrent map read and map write

 

 

posted @   明王不动心  阅读(435)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示