2.3. 字典

在业务和算法中需要使用任意类型的关联关系时,就需要使用到映射,如学号和学生的对应、名字与档案的对应等。

Go语言提供的映射关系容器为map。map使用散列表(hash)实现。

2.3.1. 1.添加关联到map并访问关联和数据

Go语言中map的定义是这样的:

map[keyType]ValueType

· KeyType为键类型
· ValueType为键对应的值类型

一个map的键和值总是成对出现的。
package main

import "fmt"

func main() {
    // 创建一个map
    scene := make(map[string]int)
    // 向map中添加映射关系
    scene["route"] = 66
    fmt.Println(scene["route"]) //66

    v := scene["route2"]
    fmt.Println(v) //0
}

另外一种声明map的方式

m := map[string]string{
    "name":   "hujianli",
    "age":    "22",
    "sex":    "man",
    "school": "dianda",
}
// name:hujianli  age:22  sex:man  school:dianda
fmt.Printf("name:%s  age:%s  sex:%s  school:%s", m["name"], m["age"], m["sex"], m["school"])

某些情况下,需要明确知道查询中某个键是否在map中存在,可以使用一种特殊的写法来实现.

看下面的代码:

v, ok := scene["route"]

例如,如果元素类型是一个数字,你可以需要区分一个已经存在的0,和不存在而返回零值的0,可以像下面这样测试:

age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }

你会经常看到将这两个结合起来使用,像这样:

if age, ok := ages["bob"]; !ok { /* ... */ }

示例

package main

import "fmt"

func main() {
    // var mydicMap map[string]string
    mydicMaps := make(map[string]string)
    mydicMaps["hujianli1"] = "A1"
    mydicMaps["hujianli2"] = "A2"
    mydicMaps["hujianli3"] = "A3"

    for k, v := range mydicMaps {
        fmt.Printf("%s %s\n", k, v)
    }
    name1, ok := mydicMaps["hu1"]
    if !ok {
        fmt.Println("----------", name1)
    }

    // 或者
    if name2, ok := mydicMaps["hu2"]; !ok {
        fmt.Println("----------", name2)
    }
}

2.3.2. 2.遍历map中的键值对–访问关联关系

m := map[string]string{
    "name":   "hujianli",
    "age":    "22",
    "sex":    "man",
    "school": "dianda",
}
for i, i2 := range m {
    fmt.Printf("key:%s value:%s\n", i, i2)
}
// 只遍历值
for _, i2 := range m {
    fmt.Printf("%s\n", i2)
}
// 只遍历键
for i := range m {
    fmt.Printf("%s\n", i)
}

如果要特定顺序的遍历结果。正确的做法是排序

2.1 排序遍历

package main

import (
    "fmt"
    "sort"
)
func main() {
    scene := make(map[string]int)
    //准备map数据
    scene["route"] = 66
    scene["brazil"] = 4
    scene["china"] = 960
    // 声明一个切片保存map数据
    var sceneList []string
    // 将map数据遍历后复制到切片中
    for key := range scene {
        sceneList = append(sceneList,key)
    }
    // 对切片进行排序
    sort.Strings(sceneList)
    // 输出
    fmt.Println(sceneList)  //[brazil china route]
}

2.3.3. 3.使用delete()函数从map中删除键值对

delete函数从map中删除一组键值对,delete函数的格式如下:

delete(map,键)
· map为要删除的map实例
· 键为要删除map键值对中的键
package main

import "fmt"

func main() {
    scene := make(map[string]int)
    //准备map数据
    scene["route"] = 66
    scene["brazil"] = 4
    scene["china"] = 960
    // 删除一个键
    delete(scene, "brazil")
    fmt.Println(scene)      //map[china:960 route:66]
    for i, i2 := range scene {
        fmt.Println(i,i2)
    }
}

2.3.4. 4.清空map中的所有元素

Go 语言中没有清空map的方法和函数。清空map的位于方法是重新make一个新的map。 Go中的并行垃圾回收效率比写一个清空函数高效多了。

2.3.5. 5.能够在并发环境中使用的map-sync.Map

package main

import (
    "fmt"
    "sync"
)

func main() {
    var scene sync.Map
    // 将键值对保存到sync.Map中
    scene.Store("green", 97)
    scene.Store("red", 100)
    scene.Store("blue", 200)

    // 从sync.Map中根据键取值
    scene.Load("red")
    //根据键删除对应的键值对
    scene.Delete("green")
    // 遍历所有的键值对
    scene.Range(func(key, value interface{}) bool {
        fmt.Println("iterate:", key, value)
        return true
    })

}

/*
iterate: red 100
iterate: blue 200
*/
package main

import (
    "fmt"
)


var opMap = func(name map[string]int) {

    for key, value := range name {
        fmt.Println(key, value)
    }

    name["Life"] = 100

    if value, ok := name["Go"]; ok {
        fmt.Println(value)
    } else {
        fmt.Println("no exists Go")

    }
    delete(name, "java")
}
func main() {
    nameMap := make(map[string]int)
    nameMap["java"] = 200
    nameMap["php"] = 100
    nameMap["python"] = 180
    nameMap["js"] = 220

    opMap(nameMap)
    fmt.Println(nameMap)

}

2.3.6. 6.在函数间传递映射

在函数间传递映射并不会制造出该映射的一个副本。实际上,当传递映射给一个函数,并对这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改。

package main

import "fmt"

func main() {
    // 创建一个映射,存储颜色以及颜色对应的十六进制代码
    colors := map[string]string{
        "AliceBlue":   "#f0f8ff",
        "Coral":       "#ff7F50",
        "DarkGray":    "#a9a9a9",
        "ForestGreen": "#228b22",
    }

    // 显示映射里的所有颜色
    for key, value := range colors {
        fmt.Printf("key is: %s value is: %s\n", key, value)
    }
    fmt.Println("--------------------------------------------------")
    // 调用函数来移除指定的键
    removeColor(colors, "Coral")

    // 显示映射里的所有颜色
    for key, value := range colors {
        fmt.Printf("Key: %s Value: %s\n", key, value)
    }

}

func removeColor(colors map[string]string, s string) {
    delete(colors, s)
}

/**
key is: AliceBlue value is: #f0f8ff
key is: Coral value is: #ff7F50
key is: DarkGray value is: #a9a9a9
key is: ForestGreen value is: #228b22
--------------------------------------------------
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22
Key: AliceBlue Value: #f0f8ff
 */

2.3.7. 7.使用map查找重复的行

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)

func main() {
    counts := make(map[string]int)
    for _, filename := range os.Args[1:] {
        data, err := ioutil.ReadFile(filename)
        if err != nil {
            fmt.Fprintf(os.Stderr, "dup3: %v\n", err)
            continue
        }
        for _, line := range strings.Split(string(data), "\n") {
            counts[line]++
        }
    }
    for line, n := range counts {
        if n > 1 {
            fmt.Printf("%d\t%s\n", n, line)
        }
    }
}

ReadFile函数返回byte类型的slice,这个slice必须被转换为string,之后才能够用strings.Split方法来进行处理。