map 介绍

  1. map是一种无序的基于key-value的数据结构,Go语言中的map引用类型必须初始化才能使用
  2. 字典(map)是一种键-值对(key-value)的无序集合
    • 一组称为值元素 value
    • 另外一组称为唯一键索引key
    • 未初始化字典的值nil。
  3. 字典是引用类型:声明如下
    • [keytype]和valuetype之间允许有空格,但是Gofmt移除了空格。
var map1 map[keytype]valuetype
  1. 声明的时候不需要知道字典的长度,字典是可以动态增长的,但最好指定容量。
  2. key可以是任意能使用**==或者!=**操作符比较的类型,这是由于在使用map中很多都需要比较key是否相等,比如获取key存储的value值,需要比较key是否一致。
    • stringintfloat
    • 所以字典切片函数不能作为key
    • 含有切片的结构体不能作为key,只能包含内建类型的struct是可以作为key的。
    • 指针和接口类型可以,尽量不使用接口,接口作为key在hash时有可能会panic,接口中存在不可比较类型时
  3. value可以是任意类型,通过使用空接口类型,可以存储任意值。但是使用这种类型作为值时需要先做一次类型断言。
  4. 字典传递给函数的代价很小,虽然通过key在字典中查找值很快。但是仍然比从数组和切片的索引中读取要慢,一般建议使用切片。
  5. 字典可以用{key1:val1, key2:val2}的描述方式来初始化,就像数组和结构体一样。字典是引用类型的,内存用make函数来分配。
var map1 = make(map[keytype]valtype)
  1. 和数组不同,字典可以根据新增的键-值(key-value)对动态的伸缩,因此它不存在固定长度或者最大限制。
  2. 也可以选择标明map的初始容量capacitymake(map[keytype]valtype, cap)
// cap 表示 map 的容量,该参数虽然不是必须的
// 应该在初始化map的时候就为其指定一个合适的容量
make(map[keyType]ValueType, [cap])
map2 := make(map[string]float32, 100) // 支持100个key/elem不扩容
  1. 当字典增长到容量上限的时候,如果在增加新的键-值对,字典的大小会自动加1。所以对于大的字典或者会快速扩张的字典,即使只是大概知道容量,也最好先标明。
  2. 在一个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
}
  1. 可以通过val1 = map1[key1]的方法获取key1对应的值val1
    • value, ok = map[key]
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. 定义字典的例子。
 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"}
  1. 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]
}
  1. 字典默认是无序的,无论是按照key还是value默认都不排序。
  2. 如果想为字典排序,需要将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语句中的值

  1. range语句中生成的数据的值是真实集合元素的副本,他们不是原有元素的引用。
  2. 意味着更新这些值将不会修改原来的数据,同时也意味着使用这些值的地址将不会得到原有数据的指针。
 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. 修改更新原有集合中的数据。
 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]
}