每个语言基本都有类似于map的容器,C++有map和unordered_map,map的底层是红黑树,unordered_map的底层是哈希表。那么GO语言中的map它的底层就是哈希表了。下面来讨论GO语言中的map的使用
定义map
可以用下面的方式直接定义
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
fmt.Println(m1)
}
其中定义的第一行,map表示变量m是一个map,[string]表示它的key是一个string,后面跟着的string表示它的值是一个string,花括号{}内部是给这个map添加的一些元素
另外也可以用make来定义
m2 := make(map[string]string)
上面创建了一个空的map,注意空的map和nil的区别
func main() {
m2 := make(map[string]string) //空map
var m3 map[string]string //nil
m2["April"] = "4" //添加成功
m3["April"] = "4" //运行时出错
fmt.Println(m2)
fmt.Println(m3)
}
用make创建出来的是一个空map,而如果像下面一行那样只是用var来声明,那么其实这个m3是一个nil,nil是map的默认零值,这个和Slice一样,往nil的map中插入数据会出错
panic: assignment to entry in nil map
所以不推荐使用var来只声明而不定义一个map,声明了还是需要用make来给map定义。
向map中添加元素
添加元素有点像C++的map重载的[]的方法,直接使用[]来像map中添加和修改元素,用过C++的map的应该很好理解
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
fmt.Println(m1)
m1["April"] = "4" //添加成功
fmt.Println(m1)
m1["January"] = "一月"
fmt.Println(m1) //修改成功
}
输出结果
map[January:1 February:2 March:3]
map[January:1 February:2 March:3 April:4]
map[January:一月 February:2 March:3 April:4] //修改成功
遍历map
range也可以用在对map的遍历,事实上许多容器都可以用range来遍历
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
m1["April"] = "4" //添加成功
for k,v := range m1 {
fmt.Println(k,v)
}
}
输出结果
January 1
February 2
March 3
April 4
range同样返回两个值,第一个表示map的key,第二个表示map的value,和数组的使用方式一样,如果我们只想要value,那么需要k的位置需要用下划线_代替。
至于为什么输出的结果顺序为什么不是有序的,即如果是有序的,那么输出的结果是按key的大小排序的(即April,February,March,Month),这里学过C++的应该了解过map和unordered_map的区别,map的底层是红黑树,遍历一棵红黑树的时候,我们遍历的时候是按大小比较遍历的,而GO的map相当于unordered_map,底层是哈希表实现的,哈希表不能保证插入进来的元素的有序性,它是无序的,但是它的插入效率要比红黑树快一点,因此就是牺牲有序性来提升效率。
获取map中的元素
获取元素同样通过[]来获取,只不过注意一下它的返回值。
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
month := m1["January"]
fmt.Println(month)
}
这边通过[]获取到了key为January的value,那么这个时候如果想要获取一个不存在的key的value会怎么样呢?
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
month := m1["April"]
fmt.Println(month)
fmt.Println(m1)
}
输出结果
map[March:3 January:1 February:2]
输出了一个空串,所以如果key不存在,那么返回的是value的默认零值。我们之后也打印了一下m1,发现m1中并没有这个April。其实学过C++的在这里地方会有一点问题,因为在这种情况下,C++的map中是会将April这个key插入到map中的。
来看一下下面的C++代码
#include <iostream>
#include <map>
using namespace std;
int main(){
map<string,string> m1;
m1["January"] = "1";
m1["February"] = "2";
m1["March"] = "3";
for(auto i : m1)
cout << i.first << ":" << i.second << endl;
cout << endl;
string month = m1["April"];
for(auto i : m1)
cout << i.first << ":" << i.second << endl;
return 0;
}
输出结果
February:2
January:1
March:3
April:
February:2
January:1
March:3
可见,GO语言在这个地方和C++就不同了,需要注意,GO只有在真正地执行添加元素的操作才会往map中添加元素,而C++只要使用到对应的key就会往map中添加这个key
另外,如果想要判断key在map中是否存在怎么做呢?GO通过下面的方式来解决
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
month,ok:= m1["April"]
fmt.Println(month,ok)
month,ok = m1["January"]
fmt.Println(month,ok)
}
输出结果
false
1 true
其实调用[]返回的是两个值,第一个是key对应的value,第二个返回值表示这个key是否存在,是一个bool类型。所以,如果我们只想判断这个key是否存在,就可以用下划线_代替前面的value不接收那个参数
删除map中的元素
删除map中的元素使用内置函数delete
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
month,ok := m1["January"]
fmt.Println(month,ok)
delete(m1,"January")
month,ok = m1["January"]
fmt.Println(month,ok)
}
输出结果
1 true
false
这样就把key为January的元素删除了
获取map中元素的个数
获取元素个数使用len内置函数
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
fmt.Println(len(m1))
}
输出结果
3
map是引用类型
func change(m1 map[string]string){
m1["January"] = "一月"
}
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
change(m1)
fmt.Println(m1)
}
输出结果
map[January:一月 February:2 March:3]
因为map是引用类型,所以它作为参数传给函数参数的时候,是引用传递,因此在函数内部对map的改变也会影响到原有的map。