学习《GO语言学习笔记》的学习笔记之5.4 数据之字典 (详解)

本文适合初学者阅读
  • 5.4 字典
  • 字典是一种使用频率极高的数据结构.
  • 字典是引用类型, 使用make函数或初始化表达语句来创建
package main

import (
	"fmt"
)

func main() {
	m := make(map[string]int)

	m["c"] = 3
	m["d"] = 4

	m2 := map[int]struct {
		x int
	}{
		1: {x: 10},
		2: {x: 200},
	}
	fmt.Println(m, m2)
}

// result out:
// map[d:4 c:3] map[1:{10} 2:{200}]
基本操作
package main

import "fmt"

func main() {
	m := map[string]int{
		"a": 1,
		"b": 2,
	}

	m["a"] = 10    // 修改字典值
	m["c"] = 20		// 新增键值对
	m["d"] = 40

	if v, ok := m["c"]; ok {   // 使用OK-idiom, 判断key是否存在, 返回值 
		println(v)
	}

	delete(m, "a")              // 删除键值对

	fmt.Println(m)
}

// result out:
// 20
// map[b:2 c:20 d:40]
  • 访问不存在的键值, 默认返回零值 , 不会引发错误. 但推荐使用ok-idiom模式,
  • 对字典迭代, 每次返回的键值次序都不相同.
  • 函数len返回当前键值对数量, cap不接受字典类型,
  • 因内存访问安全和哈希算法等原因, 字典被设计成"not addressable",所以不能直接修改value成员(结构或数组)
package main

import "fmt"

func main() {
	type user struct{
		name string
		age int
	}

	m := map[int]user{
		1: {"TOM", 18},
	}

	// m[1].age += 1   // 错误: cannot assign to m[1].age

	u := m[1]
	u.age += 1
	m[1] = u           // 设置整个value


	m2 := map[int]*user {
		1: &user{"jack", 20},
	}
	m2[1].age++          // m2[1]返回的是指针, 可透过指针修改目标对象

}

  • 不能对nil字典进行与操作, 只能读
  • 内容为空的字典,与nil是不同的
安全
  • 在迭代期间删除或新增键值是安全的
package main

import "fmt"

func main() {
	m := make(map[int]int)

	for i := 0; i < 10; i++ {
		m[i] = i + 10
	}
	fmt.Println(m)
	for k := range m {
		if k == 5 {
			m[100] = 100
		}
		delete(m, k)
		fmt.Println(k, m)
	}

}

// result out:
// map[3:13 4:14 5:15 6:16 8:18 0:10 1:11 2:12 7:17 9:19]
// 4 map[7:17 9:19 0:10 1:11 2:12 6:16 8:18 3:13 5:15]
// 5 map[6:16 8:18 3:13 7:17 9:19 100:100 0:10 1:11 2:12]
// 6 map[3:13 8:18 0:10 1:11 2:12 7:17 9:19 100:100]
// 8 map[0:10 1:11 2:12 7:17 9:19 100:100 3:13]
// 3 map[9:19 100:100 0:10 1:11 2:12 7:17]
// 1 map[2:12 7:17 9:19 100:100 0:10]
// 2 map[0:10 7:17 9:19 100:100]
// 7 map[9:19 100:100 0:10]
// 9 map[0:10 100:100]
// 100 map[0:10]
// 0 map[]
  • 运行时会对字典并发操作做出检测, 如果某个任务正在对字典进行写操作, 那么其他任务就不能对该字典执行并发操作(读写删), 否则会导致进程崩溃.
  • 可启用数据竞争检查此类问题, 它会输出详细检测信息.
  • 可用sync.RWMutex实现同步,避免读写操作同时进行.
性能
  • 字典本身就是指针包装, 传参时无须再次取地址.
  • 在创建时预先准备足够空间有助于提升性能, 减少扩张时的内存分配和重新哈希操作.
  • 对于海量小对象, 应直接用字典存储键值数据copy, 而非指针. 这有助于减少需要扫描的对象数量, 大幅缩短垃圾回收时间. 另外字典不会收缩内存, 所以适当替换成新的对象,是有必要的.
  • TODO: 以下代码未跑通
package main

import "testing"

func test() map[int]int {
	m := make(map[int]int)
	for i := 0; i < 1000; i++ {
		m[i] = i
	}
	return m
}

func testCap() map[int]int {
	m := make(map[int]int, 1000)
	for i := 0; i < 1000; i++ {
		m[i] = i
	}
	return m
}

func BenchmarkTest(b *testing.B) {
	for i := 0; i < b.N; i++ {
		test()
	}
}

func BenchmarkTestCap(b *testing.B) {
	for i := 0; i < b.N; i++ {
		testCap()
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值