前言
每一门语言都有容器,用于存储一系列具有相同特征的数据,一个容器内的数据往往由键、值对进行组合,今天带大家领略一下Go语言中的容器
一、Go语言中的容器分类
1.Map集合
语法概括:
先声明后初始化
var 集合名 map[集合键类型] 集合值类型
集合名=make(map[键类型]值类型)
直接进行初始化
var name = map[string]string{"func": "123", "name": "66"}
底层实现:
集合底层用哈希表进行实现,所以集合中存取的数据是乱序的
所以集合内一个键只能对应一个值不可以对应多个值,最新插入的会刷新旧的
迭代集合:
集合使用range进行迭代的时候,返回的值可以是一个k也可以是k,v会自动调整
一个值进行接受返回的是k,两个值接受返回的是k,v
创建一个集合,并进行迭代、删除
代码如下(示例):
name := make(map[string]string)
if name == nil {
fmt.Println("none")
}
name["France"] = "巴黎"
name["Italy"] = "罗马"
name["Japan"] = "东京"
name["India "] = "新德里"
name["France"] = "y"
//删除操作
delete(name, "France")
//迭代遍历
for k, v := range name {
fmt.Println("国家:", k, "首都:", name[k], v)
}
2.sync.Map集合
语法概括:
var 变量名 sync.Map
在sync.Map中数据以键值对的方式存储在interface{}类型中
增删改查操作:
Store()//增(增加后,如果原来的键被占用,那么会将原来的进行覆盖)
Delete()//删
Load()//查(查到数据后会将返回对应的值与true,查不到返回nil与false)
Range()//遍历所有数据,传回调函数进去
使用Range()对图进行遍历的时候需要将回调函数传进去,可以是一个匿名函数
Range函数源代码如下:
func (m *Map) Range(f func(key, value interface{}) bool) {
read, _ := m.read.Load().(readOnly)
if read.amended {
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
if read.amended {
read = readOnly{m: m.dirty}
m.read.Store(read)
m.dirty = nil
m.misses = 0
}
m.mu.Unlock()
}
for k, e := range read.m {
v, ok := e.load()
if !ok {
continue
}
if !f(k, v) {
break
}
}
}
所以传进去的函数要采用其参数指定的格式
实际操作代码如下(示例):
//声明变量
var test sync.Map
//添加数据
test.Store("France", "巴黎")
test.Store("Italy", "罗马")
test.Store("Italy", "罗罗")
fmt.Println("----------查键为France的数据-------")
fmt.Println(test.Load("France"))
fmt.Println("--------删除前的数据-------")
test.Range(func(k, v interface{}) bool {
fmt.Println(k, "的首都是", v)
return true
})
fmt.Println("----------删除后的数据--------------")
test.Delete("Italy")
test.Range(func(k, v interface{}) bool {
fmt.Println(k, "的首都是", v)
return true
})
/*
打印结果:
----------查键为France的数据-------
巴黎 true
----------删除前的数据-----------
France 的首都是 巴黎
Italy 的首都是 罗罗
----------删除后的数据--------------
France 的首都是 巴黎
*/
特点:
并发安全,但执行效率不如Map
总结
两种集合在并发情况下sync.Map更具有优势(线程安全),在非并发情况下Map更具有优势(执行效率高),涉及到并发安全问题,在叙述并发的时候还会详细介绍,对于集合使用方法大家了解这些即可。