文章目录
正文
map数据类型
0.基本介绍
一种数据结构,可以以键值对的方式存储数据,与其他语言中的字典差不多。
1. 使用中的注意事项
- key 可以是任意可以用“ ==” 或者 “!= ”操作符比较的类型,不能是slice、map还有function,因为这些类型的变量没法用“==”进行判断
- 声明后必须make初始化分配内存后才能使用;没有make时,取值不会报错,但是赋值会报panic
- make的时候可以带什么参数?长度?容量?带和不带有什么区别?性能上有什么影响?
- key-value是无序的
- 对于map类型的变量,在未make前,通过var 声明的变量是有指针的
- 在make之前,(map变量==nil)的返回值是true,也就是说,在make前,一个map变量是nil
- 同类型的map变量之间,是可以相互赋值的
- map可以无限增加元素吗——查不到相关的说明,应该是可以添加的
- map取一个不存在的键,取值并不会报错,返回一个默认值和是否存在这个key的布尔值
- 一个map变量声明后,其指针是不会变的,毕竟是引用类型,引用类型的指针不应该是会变的。——变量声明后,指正不可能变吧,但是如果是类集合的变量,其非首元素的其他元素的指针是可能变化的?——应该也不会变化,只是声明的容量的范围之外的元素的地址与容量范围内的元素的地址、容量范围外的元素之间的地址可能不连续而已?
- 并不能用cap内嵌方法求map的容量,可以用len求map长度
- 含有数组切片的结构体不能作为 key,只包含内建类型的 struct 是可以作为 key 的
- map初始化时,最好在make的时候初始化其空间,即便只是知道其大概的长度,以减少性能开销
2. 声明及make
- var先声明然后make
var dict map[string]string
dict = make(map[string]string, 3)
dict["key"] = "value"
fmt.Printf("%v", dict)
- “:=”声明并make
var value int = 10
key := "string"
dict := make(map[string]int)
dict[key] = value
fmt.Printf("%v", dict)
- 声明的时候赋值
var dict = map[string]string{
"key": "value",
}
fmt.Printf("%v", dict)
2.1 如何知道一个map变量是否已经make?
判断是否等于nil:
var maptest map[string]int
fmt.Println("the length of init map:", len(maptest))//输出the length of init map: 0
if nil == maptest {
fmt.Println("the not init map is nil")//会输出the not init map is nil
}
3. 赋值和改值
- 同类型的map变量之间,可以直接赋值,此时是引用赋值
- 赋值和改值: map[key]=value
- 如果键不存在,则会创建键,然后赋值这个刚建立的键的值
- 如果键存在,则更新值
- 如果新创建了键,则map变量的容量会增加
4. 加值
给map加值的方式和赋值、改值一样,map的特性是,如果不存在这个key则直接增加。
5. 删值
- 删除:使用内嵌函数delete(m map[Type]Type1, key Type)进行删除,If m is nil or there is no such element, delete is a no-op.
- 清空:没有清空方法,如果要清空,可以重新给map变量make一个空间,由GC去回收这部分空间
6. 取值
- 取值1:根据key直接单变量取值(不判断是否存在这个值),如果不存在这个键,则返回值的类型的零值
value := dict[key]
- 取值2:多变量取值,即取值的时候连同是否存在Key也进行判断
value , isExistKey := dict[key]
if isExistKey {
fmt.Println("key exist")
}
- 当键不存在时,返回值应该是默认值,比如string型的value,返回值是空字符串
7. 遍历
for key, value := range dict {
fmt.Println("v is ", key)
fmt.Println("e is ", value)
}
- 遍历的时候,能否进行增删操作?
- 集合类的数据,遍历的时候都不应该对集合本身进行增删操作
- 对于map来说,语法上并未禁止遍历的时候增删map,但实际上在使用for range遍历的时候进行对map的增加操作,其遍历的次数是不知道具体规律的,这次运行会遍历如一下代码,每次执行的结果都不一致:
dict := make(map[string]int)
dict["firstKey"] = 0
var i = 0
for key, value := range dict {
i++
fmt.Println("key is ", key)
fmt.Println("value is ", value)
dict["newKey"+strconv.Itoa(i)] = i
}
可能的执行结果1:
v is firstKey
e is 0
v is newKey1
e is 1
可能的执行结果2:
v is firstKey
e is 0
v is newKey1
e is 1
v is newKey2
e is 2
v is newKey3
e is 3
v is newKey4
e is 4
v is newKey5
e is 5
v is newKey6
e is 6
v is newKey7
e is 7
-
遍历的时候进行删值操作,可以顺利地把全部元素删除
-
获取所有的Key的集合或者获取value的集合,都要遍历,或者使用反射。使用反射的方式,效率很低。
8.长度和容量
仅可以使用len求长度,此时的长度是键值对的个数;
不能用cap求容量。
9. Key和Value的排序
golang的map的key是无序的,如果需要有序需遍历获取map的所有key存入切片或者数组,然后再对这个数组或者切片排序,排序后再依据这个数组或者切片去遍历map,此时就可以根据key的排序取value:
var keys []int
for k := range m {
keys = append(keys, k)
}
sort.Ints(keys)
// To perform the opertion you want
for _, k := range keys {
fmt.Println("Key:", k, "Value:", m[k])
}
//以上代码参考自https://studygolang.com/articles/10530