map 介绍#
map
是一种无序的基于key-value
的数据结构,Go
语言中的map
是引用类型,必须初始化才能使用。
- 字典(
map
)是一种键-值对(key-value
)的无序集合。
- 一组称为值元素
value
。
- 另外一组称为唯一键索引
key
。
- 未初始化字典的值
nil。
- 字典是引用类型:声明如下
- [
keytype
]和valuetype
之间允许有空格,但是Gofmt
移除了空格。
var map1 map[keytype]valuetype
- 声明的时候不需要知道字典的长度,字典是可以动态增长的,但最好指定容量。
key
可以是任意能使用**==
或者!=
**操作符比较的类型,这是由于在使用map中很多都需要比较key是否相等,比如获取key存储的value值,需要比较key是否一致。
- 如
string
、int
、float
。
- 所以字典、切片、函数不能作为
key
。
- 含有切片的结构体不能作为
key
,只能包含内建类型的struct
是可以作为key
的。
- 指针和接口类型可以,尽量不使用接口,接口作为key在hash时有可能会panic,接口中存在不可比较类型时。
value
可以是任意类型,通过使用空接口类型,可以存储任意值。但是使用这种类型作为值时需要先做一次类型断言。
- 字典传递给函数的代价很小,虽然通过
key
在字典中查找值很快。但是仍然比从数组和切片的索引中读取要慢,一般建议使用切片。
- 字典可以用
{key1:val1, key2:val2}
的描述方式来初始化,就像数组和结构体一样。字典是引用类型的,内存用make
函数来分配。
var map1 = make(map[keytype]valtype)
- 和数组不同,字典可以根据新增的键-值(
key-value
)对动态的伸缩,因此它不存在固定长度或者最大限制。
- 也可以选择标明
map
的初始容量capacity
,make(map[keytype]valtype, cap)
。
// cap 表示 map 的容量,该参数虽然不是必须的
// 应该在初始化map的时候就为其指定一个合适的容量
make(map[keyType]ValueType, [cap])
map2 := make(map[string]float32, 100) // 支持100个key/elem不扩容
- 当字典增长到容量上限的时候,如果在增加新的键-值对,字典的大小会自动加1。所以对于大的字典或者会快速扩张的字典,即使只是大概知道容量,也最好先标明。
- 在一个
nil
的切片中添加元素是没有问题的,但是对于一个字典做同样的事情将会生成一个运行时异常。由于nil
还未分配内存,所以不能使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package main
import (
"fmt"
)
func main() {
// 声明变量s,并未分配内存 默认值为 nil
// slice切片结构 struct slice {data, len, cap}
var s []int
// append函数会初始化s变量并分配内存
s = append(s, 1)
fmt.Println(s) // [1]
//var m map[string]int
//m["one"] = 1 // panic: assignment to entry in nil map
//fmt.Println(m)
// 但是使用make()创建的者不会出现类似情况
// make函数会初始化map,并分配内存
m2 := make(map[string]int, 0)
m2["a"] = 12
fmt.Println(m2) // map[a:12]
}
|
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"
)
var m3 = make(map[string]int, 8)
var m4 map[string]float64
func main() {
var m = make(map[string]int, 8)
m["a"] = 12
fmt.Println(m) // map[a:12]
var m1 map[string]string
m1["a"] = "as" // panic: assignment to entry in nil map
fmt.Println(m1)
m2 := make(map[string]int, 8)
m2["a"] = 12
fmt.Println(m2) // map[a:12]
m3["a"] = 12
fmt.Println(m3) // map[a:12]
m4["a"] = 12.3 // panic: assignment to entry in nil map
fmt.Println(m4)
i, v := m3["a"]
fmt.Println(i, v) // 12 true
}
|
- 可以通过
val1 = map1[key1]
的方法获取key1
对应的值val1
。
1
2
3
|
if _, ok := x["two"]; !ok {
fmt.Println("值不存在")
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package main
import "fmt"
func main() {
scoreMap := make(map[string]int)
scoreMap["one"] = 90
scoreMap["two"] = 100
// 如果key存在 ok为true,v为对应的值
// key不存在 ok为false,v为值类型的零值
v, ok := scoreMap["two"]
if ok {
fmt.Println(v)
} else {
fmt.Println("不存在")
}
}
|
- 定义字典的例子。
1
2
3
4
5
6
7
8
9
10
|
// 指定容量
map1 := make(map[string]string, 5)
map2 := make(map[string]string)
// 创建并初始化一个空的字典,这时没有任何元素
map3 := map[string]string{}
// 字典中有三个值
map4 := map[string]string{"a":"1", "b":"2", "c":"3"}
|
- 从
map1
中删除key1
,直接使用delete(map1, key1)
就可以。
map1
:表示要删除键值对的map
key1
:表示要删除的键值对的键
- 如果
key1
不存在,该操作不会产生错误
// 从字典map4中删除键"a"
delete(map4, "a")
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import "fmt"
func main() {
scoreMap := make(map[string]int)
scoreMap["one"] = 90
scoreMap["two"] = 100
// 从scoreMap中删除键one
delete(scoreMap, "one")
fmt.Println(scoreMap) // map[two:100]
}
|
- 字典默认是无序的,无论是按照
key
还是value
默认都不排序。
- 如果想为字典排序,需要将
key
(或者value
)复制到一个切片,再对切片排序(使用sort
包)。
按照指定顺序遍历map
#
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
33
34
35
36
37
38
39
40
41
42
43
|
package main
import (
"fmt"
"math/rand"
"sort"
"strconv"
"time"
)
func main() {
// 初始化随机数种子
rand.Seed(time.Now().UnixNano())
var scoreMap = make(map[string]int, 200)
// 生成数据
for i := 0; i < 100; i++ {
// 生成 0~99 的随机整数
key := ""
if i < 10 {
key = "0" + strconv.Itoa(i)
} else {
key = strconv.Itoa(i)
}
scoreMap[key] = rand.Intn(100)
}
// 取出map中的所有key存入切片keys
var keys = make([]string, 0, 200)
for key := range scoreMap {
// 把map的key存入[]string切片
keys = append(keys, key)
}
// 对切片进行排序
sort.Strings(keys)
// 按照排序后的key遍历map
for _, key := range keys {
fmt.Println(key, scoreMap[key])
}
}
|
元素为map
类型的切片#
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
33
34
35
36
|
package main
import "fmt"
func main() {
var mapSlice = make([]map[string]string, 3) // 元素map为nil,需要初始化
for index, value := range mapSlice {
fmt.Printf("index:%d value:%#v\n", index, value)
}
/*
* index:0 value:map[string]string(nil)
* index:1 value:map[string]string(nil)
* index:2 value:map[string]string(nil)
*/
fmt.Println("after init")
// 对切片中的map元素进行初始化 并指定容量为10
mapSlice[0] = make(map[string]string, 10)
fmt.Println(mapSlice, mapSlice[0]) // [map[] map[] map[]] map[]
mapSlice[0]["name"] = "name"
mapSlice[0]["password"] = "password"
mapSlice[0]["address"] = "address"
fmt.Println(mapSlice) // [map[address:address name:name password:password] map[] map[]]
for index, value := range mapSlice {
fmt.Printf("index:%d value:%#v\n", index, value)
}
/*
* index:0 value:map[address:address name:name password:password]
* index:1 value:map[string]string(nil)
* index:2 value:map[string]string(nil)
*/
}
|
值为切片类型的map
#
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() {
var sliceMap = make(map[string][]string, 3)
fmt.Printf("%#v\n", sliceMap) // map[string][]string{}
fmt.Println("after init") // after init
key := "中国"
value, ok := sliceMap[key]
fmt.Printf("%#v, %#v\n", value, ok) // []string(nil), false
if !ok {
// 初始化 切片[]string 长度0 容量2
value = make([]string, 0, 2)
}
// 切片添加元素
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap) // map[中国:[北京 上海]]
}
|
range
语句中的值#
- 在
range
语句中生成的数据的值是真实集合元素的副本,他们不是原有元素的引用。
- 意味着更新这些值将不会修改原来的数据,同时也意味着使用这些值的地址将不会得到原有数据的指针。
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
33
34
|
package main
import (
"fmt"
)
func main() {
data := []int{1, 2, 3}
for _, v := range data {
v *= 10
}
fmt.Println("data:", data) // data: [1 2 3]
scoreMap := make(map[string]int)
scoreMap["one"] = 90
scoreMap["two"] = 100
for k := range scoreMap {
fmt.Println(k)
}
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
/*
data: [1 2 3]
one
two
one 90
two 100
*/
|
- 修改更新原有集合中的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package main
import (
"fmt"
)
func main() {
data := []int{1, 2, 3}
for i := range data {
data[i] *= 10
}
fmt.Println("data:", data) // data: [10 20 30]
}
|