map 是一种特殊的数据结构:一种元素对(pair)的无序集合,pair 的一个元素是 key,对应的另一个元素是 value,所以这个结构也称为关联数组或字典。这是一种快速寻找值的理想结构:给定 key,对应的value 可以迅速定位。

内部实现

Map是给予散列表来实现,就是我们常说的Hash表,所以我们每次迭代Map的时候,打印的Key和Value是无序的,每次迭代的都不一样,即使我们按照一定的顺序存在也不行。

Map的散列表包含一组桶,每次存储和查找键值对的时候,都要先选择一个桶。如何选择桶呢?就是把指定的键传给散列函数,就可以索引到相应的桶了,进而找到对应的键值。

这种方式的好处在于,存储的数据越多,索引分布越均匀,所以我们访问键值对的速度也就越快,当然存储的细节还有很多,大家可以参考Hash相关的知识,这里我们只要记住Map存储的是无序的键值对集合

声明map

map 是引用类型,可以使用如下声明:

var variableName map[keyType]valueType

[keyType]valueType之间允许有空格,但是gofmt移除了空格

在声明的时候不需要知道 map 的长度,map 是可以动态增长的。

未初始化的 map 的值是 nil。

key 可以是任意可以用 == 或者 != 操作符比较的类型,比如 string、int、float。所以数组、切片和结构体不能作为 key,但是指针和接口类型可以。如果要用结构体作为 key 可以提供 Key() 和 Hash() 方法,这样可以通过结构体的域计算出唯一的数字或者字符串的 key。

value 可以是任意类型的;通过使用空接口类型,我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言。

map 传递给函数的代价很小: 在 32 位机器上占 4 个字节,64 位机器上占 8 个字节,无论实际上存储了多少数据。通过 key 在 map 中寻找值是很快的,比线性查找快得多,但是仍然比从数组和切片的索引中直接读取要慢 100 倍;所以如果你很在乎性能的话还是建议用切片来解决问题。

示例:

var map1 map[string]int

初始化map

Map的创建有make函数,Map字面量。make函数我们用它创建过切片,除此之外,它还可以用来创建Map。

map 是引用类型的: 内存用 make 方法来分配。

dict := make(map[string]int)

示例中创建了一个键类型为string的,值类型为int的map。现在创建好之后,这个map是空的,里面什么都没有,我们给存储一个键值对。

dict := make(map[string]int)
dict["itbsl"] = 25

存储了一个Key为itbsl的,Value为25的键值对数据。

此外还有一种使用map字面量的方式创建和初始化map,对于上面的例子,我们可以同等实现。

dict := map[string]int{"itbsl": 25}

使用一个大括号进行初始化,键值对通过:分开,如果要同时初始化多个键值对,使用逗号分割。

dict := map[string]int{"itbsl":25,"kevin": 50}

当然我们可以不指定任何键值对,也就是一个空map。

dict := map[string]int{}

不管怎么样,使用map的字面量创建一定要带上大括号。如果我们要创建一个nil的Map怎么做呢?nil的Map是未初始化的,所以我们可以只声明一个变量,既不能使用map字面量,也不能使用make函数分配内存。

var dict map[string]int

这样就好了,但是这样我们是不能操作存储键值对的,必须要初始化后才可以,比如使用make函数,为其开启一块可以存储数据的内存,也就是初始化。

var dict map[string]int
dict = make(map[string]int)
dict["itbsl"] = 25
fmt.Println(dict)

使用Map

Map的使用很简单,和数组切片差不多,数组切片是使用索引,Map是通过键。

dict := make(map[string]int)
dict["itbsl"] = 25

以上示例,如果键itbsl存在,则对其值修改,如果不存在,则新增这个键值对。

获取一个Map键的值也很简单,和存储差不多,还是给予上面的例子。

age := dict["itbsl"]

在Go Map中,如果我们获取一个不存在的键的值,也是可以的,返回的是值类型的零值,这样就会导致我们不知道是真的存在一个为零值的键值对呢,还是说这个键值对就不存在。对此,Map为我们提供了检测一个键值对是否存在的方法。

age,exists := dict["itbsl"]

看这个例子,和获取键的值没有太大区别,只是多了一个返回值。第一个返回值是键的值;第二个返回值标记这个键是否存在,这是一个boolean类型的变量,我们判断它就知道该键是否存在了。这也是Go多值返回的好处。

如果我们想删除一个Map中的键值对,可以使用Go内置的delete函数。

delete(dict,"itbsl")

delete函数接受两个参数,第一个是要操作的Map,第二个是要删除的Map的键。

delete函数删除不存在的键也是可以的,只是没有任何作用。

想要遍历Map的话,可以使用for range风格的循环,和遍历切片一样。

dict := map[string]int{"itbsl": 25}
for key, value := range dict {
fmt.Println(key, value)
}

这里的range 返回两个值,第一个是Map的键,第二个是Map的键对应的值。这里再次强调,这种遍历是无序的,也就是键值对不会按既定的数据出现,如果想安顺序遍历,可以先对Map中的键排序,然后遍历排序好的键,把对应的值取出来,下面看个例子就明白了。

func main() {
dict := map[string]int{"kevin": 40, "itbsl": 25}
var names []string
for name := range dict {
names = append(names, name)
}
sort.Strings(names) //排序
for _, key := range names {
fmt.Println(key, dict[key])
}
}

这个例子里有个技巧,range 一个Map的时候,也可以使用一个返回值,这个默认的返回值就是Map的键。

map容量

和数组不同,map 可以根据新增的 key-value 对动态的伸缩,因此它不存在固定长度或者最大限制。但是你也可以选择标明 map 的初始容量 capacity ,就像这样: make(map[keytype]valuetype, cap) 。例如:

map2 := make(map[string]float, 100)

当 map 增长到容量上限的时候,如果再增加新的 key-value 对,map 的大小会自动加 1。所以出于性能的考虑,对于大的 map 或者会快速扩张的 map,即使只是大概知道容量,也最好先标明。

map的排序

map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。

如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序,然后可以使用切片的 for-range 方法打印出所有的 key 和 value。

在函数间传递Map

函数间传递Map是不会拷贝一个该Map的副本的,也就是说如果一个Map传递给一个函数,该函数对这个Map做了修改,那么这个Map的所有引用,都会感知到这个修改。

func main() {
dict := map[string]int{"kevin": 40, "itbsl": 25}
modify(dict)
fmt.Println(dict["kevin"])
fmt.Printf("main函数dict的地址为: %p\n", dict)
} func modify(dict map[string]int) {
dict["kevin"] = 10
fmt.Printf("modify函数dict的地址为: %p\n", dict)
}

输出结果为:

modify函数dict的地址为: 0xc000076120
10
main函数dict的地址为: 0xc000076120

上面这个例子输出的结果是10,也就是说已经被函数给修改了,可以证明传递的并不是一个Map的副本。这个特性和切片是类似的,这样就会更高,因为复制整个Map的代价太大了。

Go语言map的更多相关文章

  1. Go语言Map的使用

    Go 语言Map(集合) Map 是一种无序的键值对的集合.Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值. Map 是一种集合,所以我们可以像迭代数组和切片那样 ...

  2. go语言---map

    go语言---map https://blog.csdn.net/cyk2396/article/details/78890185 一.map的用法: type PersonDB struct { I ...

  3. Go 语言 map (映射)

    1.Go 语言中 map 的定义及初始化: map[Key_Type]Value_Type scence := make(map[string]int) 2.Go 语言的遍历: scene := ma ...

  4. Go语言 map的实现

    Go中的map在底层是用哈希表实现的,你可以在 $GOROOT/src/pkg/runtime/hashmap.goc 找到它的实现. 数据结构 哈希表的数据结构中一些关键的域如下所示: struct ...

  5. Go语言 map遍历

    1 遍历顺序 Go语言里的map,是不保证遍历顺序的(这一点很好理解).甚至同样内容的map,两次遍历的顺序,也可能不一样.下面是一个例子: m := map[string]int{"a1& ...

  6. 【转】GO语言map类型interface{}转换踩坑小记

    原文:https://www.az1314.cn/art/69 ------------------------------------------ mapA := make([string]inte ...

  7. Go 语言Map(集合)

    Map 是一种无序的键值对的集合.Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值. Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它.不过,Map 是无 ...

  8. go语言map操作

    package main import "fmt" func main() { var m = map[int]string{ 90: "优秀", 80: &q ...

  9. Go语言map映射的基本操作

    对比python, java都有对应的字典类型及哈希类型. package main import ( "fmt" ) //main is the entry of the pro ...

随机推荐

  1. 1. NES简介

    NES(Nintendo Entertainment System)简介 NES是北美地区对任天堂发行的第三代家用游戏机的简称. 1.CPU NES使用一颗理光[1]制造的8位2A03 NMOS处理器 ...

  2. 除非你是BAT,前端开发中最好少造轮子

    站在前人的肩膀上 HTML.CSS.JavaScript是前端的根基,这是无可否认的事实.正如一辆车当然都是由一堆钢板和螺钉组成的,但是现在还有人拎着个锤子敲敲打打的造车吗?李书福说过,“汽车不过是四 ...

  3. 一个简单地template模板

    之前的项目中用到了artTemplate模板,感觉挺有意思,于是查看相关资料,自己动手写了个简单地template模板插件.虽然会有一些不足,但也是自己的一番心血.主体代码如下 /* * 一个简单地t ...

  4. 输入ip和端口号python脚本py文件

    1.利用.py文件进行打开URL,编辑以下文件代码保存文件为login.py: # _*_ coding: cp936 _*__all_ = ['ip','port','root_id']ip='10 ...

  5. 机器学习之支持向量机(SVM)学习笔记

    支持向量机是一种二分类算法,算法目的是找到一个最佳超平面将属于不同种类的数据分隔开.当有新数据输入时,判断该数据在超平面的哪一侧,继而决定其类别. 具体实现思路: 训练过程即找到最佳的分隔超平面的过程 ...

  6. centos服务器监控 服务器虚拟机里面的客户端zabbix-agent安装

    开启zabbix要用的端口, 也可以关闭服务器的防火 [html] view plain copy #vim /etc/sysconfig/iptables -A INPUT -m state --s ...

  7. css firefox火狐浏览器下的兼容性问题

    1.DOCTYPE 影响 CSS 处理 2.FF: div 设置 margin-left, margin-right 为 auto 时已经居中, IE 不行 3.FF: body 设置 text-al ...

  8. AX_CreateAndPostInventJournal

    static void CreateAndPostInventJournal(Args _args) { InventJournalTable inventJournalTableLocal; Inv ...

  9. VB编程中的“Abs”是什么意思?

    c = Val(Text1.Text) '将Text1中的值赋给cIf c = Abs(a - b) Then 'Abs(a - b)是a和b间的差(正数),判断c是否等于该差值f = f + 10 ...

  10. oracle中的日期函数的使用

    TO_DATE格式(以时间:2007-11-02   13:45:25为例) Year:               yy two digits 两位年                显示值:07   ...