go语言中映射的实现原理具体是怎么样的

Yang 220

go语言中映射的实现原理具体分为下列几步:一、映射的实现;二、元素赋值;三、查找与遍历;四、元素删除;五、将映射传进函数。映射是一种数据结构, 用于存储一系列无序的键值对。映射功能强大的地方是,能够基于键快速检索数据。键就像索引一样,指向与该键关联的值。 与C++、Java不一 样,Go使用映射(map)不需要引入任何库。

一、映射的实现

map(key:value) k:v key:除了切片等引用类型,其他类型即可 value:可以是所有类型的值

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

因为映射也是一个数据集合, 所以也可以使用类似处理数组和切片的方式来迭代映射中的元素。但映射是无序集合,所以即使以同样的顺序保存键值对,每次迭代映射时,元索顺序也可能不一样。无序的原因是映射的本质使用了散列表。

map在底层是用哈希(hash)表实现的,在C:\Program Files\Go\srclhashlmaphashlmaphash.go, map是一个hash数组列表,由一个个bucket组成,示意图如下:

每一个元素都被称为bucket的结构体,每一个bucket可以保存8个键值对, 所有元索被hash算法填入到数组的 bucket中,bucket填满后, 将通过一个overflow指针来扩 展一个bucket, 从来形成链表,以此来解决hash冲突的 问题,map就是一个bucket指针型的一 维数组。 创建map语法如下:

/* 声明变量,默认 map 是 nil */

var map_variable map[key_data_type]value_data_type

/* 使用 make 函数 */

map_variable := make(map[key_data_type]value_data_type)

//创建一个映射, 键的类型string,值的类型int

dict: =make(map[string ]int)

//或者创建一个映射,键值类型都是string,并对两个键值对进行初始化

dict:=map[string]string{“name”:”zhangsan”,”address”: “nanjing”}

判断空映射

package main

import (

    “fmt”

)

//映射

func main() {

    //映射的声明

    // a := make(map[string]string)

    //初始化

    a := map[string]int{“name”: 20, “jiage”: 30}

    fmt.Println(a)

    //判断空映射

    if len(a) == 0 {

        fmt.Println(“空映射”)

    } else {

        fmt.Println(“输出元素个数”, len(a))

    }

}

[Running]gorun”f:\goProject\src\dev_code\day13\example1\main\main.go”

map[jiage:30 name:20]

输出元素个数 2

[Done] exited with code=0 in 0.465 seconds

映射的键可以是任何值,这个值的类型并不限制,内置类型或者结构体都可以,需要确定这个值可以使用==运算符做比较。需要注意的是,切片、函数以及包含切片的结构类型由于是引用类型,均不能作为映射的键。

二、元素赋值

指定适当类型的键并给这个键赋一个值就完成 了映射的键值对赋值。 示例:

//创建一个空映射,用来存储颜色以及颜色对应的十六进制代码

colors :=map[string]string{}

//将red的代码加入到映射

colors[“red”]=” #DA1337“

与切片类似,通过声明-个末初始化的映射可以创建一个值为nil的映射。 nil映射不能用于存储键值对,否则,会产生语言运行时错误。

//创建一个nil映射

var colors map[string]string

//将red的代码加入到映射

colors[“red”] =“#da1337″

运行报错:

panic: assignment to entry in nil map

goroutine 1 [running]:

package main

import “fmt”

//映射赋值

func main() {

    colors := map[string]string{}

    //赋值直接定义键值对,自动添加,键名少数,值不是少数可以相同

    colors[“red”] = “红色”

    colors[“blue”] = “蓝色”

    colors[“red1”] = “红色”

    colors[“”] = “无色” //空字符串也可以作为键

    fmt.Println(colors)

    colors[“red”] = “红”

    fmt.Println(colors)

}

[Running]gorun”f:\goProject\src\dev_code\day13\example2\main\main.go”

map[:无色 blue:蓝色 red:红色 red1:红色]

map[:无色 blue:蓝色 red:红 red1:红色]

[Done] exited with code=0 in 0.486 seconds

三、查找与遍历

从映射取值时有两种方式。 名列前茅种方式是获得值以及一个表达这个值是否存在的标志。 示例:

package main

import “fmt”

//映射赋值

func main() {

    colors := map[string]string{}

    colors[“red”] = “红色”

    colors[“blue”] = “蓝色”

    //名列前茅种方式获取值并且判断值是否存在

    value, exe := colors[“blue”]

    fmt.Printf(“blue的值是%s,是否存在%b”, value, exe)

}

[Running] go run “f:\goProject\src\dev_code\day13\example2\main\main.go”

blue的值是蓝色,是否存在%!b(bool=true)

[Done] exited with code=0 in 0.442 seconds

第二种方式是,只返回键对应的值,再判断这个值是否有零值以此来确定键是否存在。这种方式只能用在映射存储的值都是非零值的情况。

package main

import “fmt”

func main() {

    //第二种方式获取值

    a := map[string]int{“1”: 10, “2”: 20, “3”: 30}

    value := a[“2”]

    if value == 0 {

        fmt.Println(“不存在”)

    } else {

        fmt.Println(value)

    }

}

[Running] go run “f:\goProject\src\dev_code\day13\example3\main\main.go”

[Done] exited with code=0 in 0.442 seconds

如果键值有0值会影响判断

package main

import “fmt”

func main() {

    //第二种方式获取值

    a := map[string]int{“1”: 10, “2”: 20, “3”: 30, “4”: 0}

    value := a[“4”]

    if value == 0 {

        fmt.Println(“不存在”)

    } else {

        fmt.Println(value)

    }

}

[Running] go run “f:\goProject\src\dev_code\day13\example3\main\main.go”

不存在

[Done] exited with code=0 in 0.457 seconds

通过键来索引映射时,即便这个键不存在也会返回该值对应的类型的零值。

示例:使用range可以迭代映射里的所有值。

package main

import “fmt”

func main() {

    //遍历map中所有值

    a := map[string]int{“1”: 10, “2”: 20, “3”: 30, “4”: 0}

    for k, v := range a {

        fmt.Printf(“key=%s,value=%d\n”, k, v)

    }

}

[Running] go run “f:\goProject\src\dev_code\day13\example3\main\main.go”

key=3,value=30

key=4,value=0

key=1,value=10

key=2,value=20

[Done] exited with code=0 in 0.451 seconds

四、元素删除

delete() 函数用于删除map集合的元素, 参数为 map 和其对应的 key。

delete( a ,”2″)从a中删除键为2的键值对。如果2这个键不存在,那么这个调用将什么都不发生,也不会有任何副作用。但是如果传入的map变量的值是nil,该调用将导致程序抛出异常(panic)。

实例如下:

package main

import “fmt”

func main() {

    a := map[string]int{“1”: 10, “2”: 20, “3”: 30, “4”: 0}

    //遍历map中所有值

    for k, v := range a {

        fmt.Printf(“key=%s,value=%d\n”, k, v)

    }

    //删除元素

    delete(a, “2”)

    fmt.Println(“——删除后——“)

    for k, v := range a {

        fmt.Printf(“key=%s,value=%d\n”, k, v)

    }

}

[Running] go run “f:\goProject\src\dev_code\day13\example3\main\main.go”

key=1,value=10

key=2,value=20

key=3,value=30

key=4,value=0

——删除后——

key=3,value=30

key=4,value=0

key=1,value=10

[Done] exited with code=0 in 0.454 seconds

五、将映射传进函数

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

示例:

package main

import “fmt”

func main() {

    //遍历map中所有值

    a := map[string]int{“1”: 10, “2”: 20, “3”: 30, “4”: 0}

    for k, v := range a {

        fmt.Printf(“key=%s,value=%d\n”, k, v)

    }

    //映射传参处理

    test(a)

    fmt.Println(“——处理后——“)

    for k, v := range a {

        fmt.Printf(“key=%s,value=%d\n”, k, v)

    }

}

func test(m map[string]int) {

    m[“2”] = 40

}

[Running] go run “f:\goProject\src\dev_code\day13\example3\main\main.go”

key=1,value=10

key=2,value=20

key=3,value=30

key=4,value=0

——处理后——

key=1,value=10    //map是引用类型

key=2,value=40

key=3,value=30

key=4,value=0

[Done] exited with code=0 in 0.461 seconds

回复

我来回复
  • 暂无回复内容

注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部