golang 数据三 (字典)

golang基本数据结构Map也叫字典,字典的声明格式如下: map[KeyType]ValueType

字典是无序键值对集合,字典要求KeyType必须是支持相等运算符(==,!=)的数据类型,比如:数字、字符串、指针、数组、结构体以及对应的接口类型,而ValueType可以是任意类型,字典也是引用类型,使用make函数或者初始化表达式语句来创建。比如:

{
m:=make(map[string]int)//创建一个字典
m["a"]=1
m["b"]=2

m2:=map[int]struct{//匿名结构体
xint
}{
1:{x:100},2:{x:200},}
fmt.Println(m,m2)
}


字典的基本操作

比如:

{
m:=make(map[string]int)
m["route"]=66//添加key
i:=m["route"]//读取key
j:=m["root"]//thevaluetypeisint,sothezerovalueis0
n:=len(m)//获取长度
//n:=cap(m)//???引发一个思考
delete(m,"route")//删除key
}

如果访问不存在的键,不会引发错误,而会认返回ValueType的零值,那么还能否根据零值来判断key的存在呢?

在读取map操作时,可以使用双返回值来正常读取map,比如:

{
j,ok:=m["root"]
}
说明:
ok是个bool类型变量,如果key真实存在,则ok的值为true,反之为false,如果你只是想测试key是否存在而不
获取值的话,可以使用忽略字符"_"。

修改map值操作时,因为受内存访问安全和哈希算法等缘故,字典被设计成"not adrressable",因此不能直接修改value成员(结构体或数组)。比如:

{
m:=map[int]user{
1:{
name:"tom",age:19},}
m[1].age=1//cannotassigntostructfieldm[1].ageinmap
}

但是有两种方式可以实现直接修改map的value成员:

  1. 是对整个value进行重新复制

  2. 声明map时valueType为指针类型

{
m:=map[int]user{
1:{
name:"tom",}
u:=m[1]
u.age=1
m[1]=u
}
{
m:=map[int]*user{
1:{
name:"tom",}
m[1].age+=1
}

不能对nil字典进行写操作,但是可以读,比如:

{
varmmap[string]int
//p:=m["a"]//ok
m["a"]=1//panic:assignmenttoentryinnilmap
}

map遍历:

{
//varm=make(map[string]int)
varm=map[string]int{}
m["route"]=66
m["root"]=67
forkey,value:=rangem{
fmt.Println("Key:",key,"Value:",value)
}
}

因为map是无序的,如果想按照有序key输出的话,可以先把所有的key取出,然后对key进行排序,再遍历map,比如:

{
m:=make(map[int]int)
varkeys[]int
fori:=0;i<=5;i++{
m[i]=i
}

fork,v:=rangem{
fmt.Println("Key:",k,v)
}
fork:=rangem{
keys=append(keys,k)
}
sort.Ints(keys)
for_,k:=rangekeys{
fmt.Println("Key:",m[k])
}
}

并发

字典不是并发安全的数据结构,如果某个任务正在对字典进行写操作,那么其他任务就不能对该字典执行并发操作(读、写、删除),否则会导致程序崩溃,比如:

m:=make(map[string]int)
gofunc(){
for{
m["a"]+=1
time.Sleep(time.Microsecond)
}
}()

gofunc(){
for{
_=m["b"]
time.Sleep(time.Microsecond)
}
}()
select{}
}
输出:
fatalerror:concurrentmapreadandmapwrite

GO语言编译器提供了这种问题(竞争)的检测方式,比如:

#gorun-racefile.go

安全

可以使用 sync.RWMutex 实现同步,避免并发环境多goroutings同时读写操作,继续完善上面的例子,比如:

{
varlock=new(sync.RWMutex)
m:=make(map[string]int)
gofunc(){
for{
lock.Lock()
m["a"]++
lock.Unlock()
time.Sleep(time.Microsecond)
}
}()

gofunc(){
for{
lock.RLock()
_=m["b"]
lock.RUnlock()
time.Sleep(time.Microsecond)
}
}()
select{}
}

性能

在创建字典时预先准备足够的空间有助于提升性能,减少扩张时引发内存动态分配和重复哈希操作,比如:

packagemain

import"testing"
import"fmt"

functest()map[int]int{
	m:=make(map[int]int)
	fori:=0;i<1000;i++{
		m[i]=1
	}
	returnm
}

functestCap()map[int]int{
	m:=make(map[int]int,1000)
	fori:=0;i<1000;i++{
		m[i]=1
	}
	returnm
}


funcBenchmarkTest(t*testing.B){
	fori:=0;i<t.N;i++{
		test()
	}
}

funcBenchmarkTestCap(t*testing.B){
	fori:=0;i<t.N;i++{
		testCap()
	}
}

funcmain(){
	resTest:=testing.Benchmark(BenchmarkTest)
	fmt.Printf("BenchmarkTest\t%d,%dns/op,%dallocs/op,%dB/op\n",resTest.N,resTest.NsPerOp(),resTest.AllocsPerOp(),resTest.AllocedBytesPerOp())
	resTest=testing.Benchmark(BenchmarkTestCap)
	fmt.Printf("BenchmarkTestCap\t%d,resTest.AllocedBytesPerOp())
}
输出:
#gorunconmap.go
BenchmarkTest	10000,160203ns/op,98allocs/op,89556B/op
BenchmarkTestCap20000,65478ns/op,12allocs/op,41825B/op


借鉴:<<雨痕笔记>>

相关文章

什么是Go的接口? 接口可以说是一种类型,可以粗略的理解为他...
1、Golang指针 在介绍Golang指针隐式间接引用前,先简单说下...
1、概述 1.1&#160;Protocol buffers定义 Protocol buffe...
判断文件是否存在,需要用到"os"包中的两个函数: os.Stat(...
1、编译环境 OS :Loongnix-Server Linux release 8.3 CPU指...
1、概述 Golang是一种强类型语言,虽然在代码中经常看到i:=1...