文章目录
Map介绍
Map介绍
- 在Go中,map是哈希表的引用,是一种key-value数据结构。map类型写作map[K]V,其中K和V分别对应key和value的类型。
- map中所有的key都是相同的类型,所有的value也是相同的类型,但key和value可以是不同的类型,value通常使用自定义类型。
- map内部需要通过判断两个key是否相等以确保每个键的唯一性,因此key的数据类型必须是可比较,如果key是自定义类型,则要求自定义类型中的所有字段是可比较的。
map的示意图如下:
map中仅包含一个指向底层哈希表的指针,属于引用类型,当map类型变量进行赋值或传参时,本质就是将map中指向哈希表的指针的值进行拷贝,因此最终两个map变量底层指向的是同一个哈希表,其中一个变量对哈希表修改会影响到另一个map变量。如下:
Map的定义方式
方式一:make map
在定义map时,可以通过make创建指定初始容量的map。如下:
package main
import("fmt""unsafe")funcmain(){// 方式一:make mapvar m =make(map[string]string,10)
fmt.Printf("m = %v\n", m)// m = map[]
fmt.Printf("m len = %v\n",len(m))// m len = 0
fmt.Printf("m size = %d\n", unsafe.Sizeof(m))// m size = 8}
说明一下:
- map定义后需要先通过make函数分配内存空间,然后才能使用。make是Go中的内建函数,可以用于分配并初始化一个map,其第一个参数表示map的类型,第二个参数表示初始容量,第二个参数若省略则使用默认的初始容量。
- 在调用make函数创建map时提供初始容量,以便底层哈希表开辟足够多的哈希桶,这样可以避免后续在插入键值对时哈希表的频繁扩容(重新哈希),从而提高性能。
- 通过len函数可以获取map中键值对的数量,但无法通过cap函数获取map的容量,map底层的哈希表会自动扩容以适应需要存储的键值对数量。
方式二:指定具体的键值对
在定义map时,也可以直接指定具体的键值对。如下:
package main
import("fmt")funcmain(){// 方式二:指定具体的键值对var m =map[string]string{"四川省":"川","陕西省":"陕","广东省":"粤",}
fmt.Printf("m = %v\n", m)// m = map[四川省:川 广东省:粤 陕西省:陕]
fmt.Printf("m len = %d\n",len(m))// m len = 3}
说明一下:
- 通过指定具体键值对创建map时,Go运行时也会在底层创建对应的哈希表,并将指定的键值对插入到哈希表中。
Map的增删查改
新增和修改Map元素
新增和修改Map元素
Go中通过
map名[key] = value
的方式在map中新增和修改键值对。如下:
package main
import"fmt"funcmain(){// 新增和修改Map元素var m =map[string]string{"四川省":"川","陕西省":"陕","广东省":"粤",}
m["浙江省"]="浙"// 新增
m["四川省"]="川~"// 修改
fmt.Printf("m = %v\n", m)// m = map[四川省:川~ 广东省:粤 浙江省:浙 陕西省:陕]
fmt.Printf("m len = %d\n",len(m))// m len = 4}
说明一下:
- map中各个键值对的key值不能重复,在以
map名[key] = value
的方式操作map中的键值对时,如果对应的key值不存在则视为向map中新增键值对,如果对应的key值存在则视为修改map中对应键值对的value。
查找Map元素
查找Map元素
Go中通过
map名[key]
的方式查找map中的元素。如下:
package main
import"fmt"funcmain(){// 查找Map元素var m =map[string]string{"四川省":"川","陕西省":"陕","广东省":"粤",}var key ="四川省"
value, ok := m[key]// 查找if!ok {
fmt.Printf("没有key为%s的键值对\n", key)}else{
fmt.Printf("%s的简称为%s\n", key, value)// 四川省的简称为川}}
说明一下:
- 通过
map名[key]
的方式访问map中的元素将会得到两个值,如果map中存在对应key值的键值对,那么第一个值则是与key对应的value,第二个值为true;如果map中不存在对应key值的键值对,那么第一个值将是value对应类型的默认值,第二个值为false。 - 通过
map名[key]
的方式访问map中的元素时,可以根据得到的第二个值来判断map中是否存在对应key值的键值对,也可以选择只用一个变量来接收其返回值,相当于忽略了它的第二个返回值。
删除Map元素
删除Map元素
Go中通过delete函数删除map中指定key值的键值对。如下:
package main
import"fmt"funcmain(){// 删除Map元素var m =map[string]string{"四川省":"川","陕西省":"陕","广东省":"粤",}delete(m,"广东省")// 删除
fmt.Printf("m = %v\n", m)// m = map[四川省:川 陕西省:陕]}
说明一下:
- delete是Go中的内建函数,用于删除map中指定key值的键值对,如果map中不存在对应的键值对,则delete不进行任何操作。
- Go中没有提供删除map中所有键值对的方法,如果希望删除map中所有的键值对,可以遍历map中的键值对并逐个进行删除,或者通过make函数重新为当前map分配内存空间,此时原先map底层的哈希表将由GC回收。
遍历Map元素
遍历Map元素
Go中通过for range循环的方式对map元素的遍历。如下:
package main
import"fmt"funcmain(){// 遍历Map元素var m =map[string]string{"四川省":"川","陕西省":"陕","广东省":"粤",}// 遍历map元素for key, value :=range m {
fmt.Printf("<key:%s, value:%s>\n", key, value)}}
说明一下:
- 在for range循环中遍历map时,每次迭代会返回两个值,第一个是当前键值对的key,第二个是当前键值对的value,当遍历结束后会自动退出for range循环。
- map中的键值对是无序的,每次遍历map得到的键值对序列都是不可预测的。
Map元素排序
Map元素排序
map中的元素是无序的,想要按照key值遍历键值对,可以采用以下方式:
- 获取map中所有键值对的key,得到keys序列。
- 根据需求对keys序列进行排序。
- 遍历keys序列,根据key值访问map中的键值对。
使用案例如下:
package main
import("fmt""sort")funcmain(){// map元素排序var m =map[int]string{1:"周一",7:"周日",3:"周三",2:"周二",5:"周五",4:"周四",6:"周六",}// 1、获取map中所有的keyvar keys []intfor key,_:=range m {
keys =append(keys, key)}// 2、对所有的key进行排序
sort.Ints(keys)// 3、遍历有序的key,根据key访问其valuefor_, key :=range keys {
fmt.Printf("<key:%d, value:%s>\n", key, m[key])}}
运行程序后可以看到,输出的键值对信息是按key值排序的。如下:
说明一下:
- Ints是sort包中的一个函数,用于对int类型的切片按升序进行排序。
- sort包中还有对其他类型切片排序的函数,比如Float64s、Strings等。
Map切片
Map切片
如果一个切片中存储元素的数据类型是map,那么我们称之为map切片。如下:
package main
import"fmt"funcAddStudent(students []map[string]string,
name string, gender string, class string)[]map[string]string{var student =map[string]string{"name": name,"gender": gender,"class": class,}
students =append(students, student)return students
}funcmain(){// map切片var students []map[string]string
students =AddStudent(students,"Alice","female","class 2")
students =AddStudent(students,"Bob","male","class 1")for i :=0; i <len(students); i++{
fmt.Printf("第%d个学生的信息如下:\n", i+1)for key, value :=range students[i]{
fmt.Printf("\t%s: %s\n", key, value)}}}
上述代码中定义了一个map切片,切片中的每一个map存储着一个学生的信息,我们通过AddStudent函数向map切片中添加了两个学生的信息,并通过遍历切片中的map输出了学生的信息。运行结果如下:
说明一下:
- 通过append函数向切片末尾追加元素时,append函数会对切片进行检测,如果该切片底层没有引用任何数组(nil切片),那么会先给切片分配一个底层数组,然后再将元素追加到切片中。
- 切片是引用类型,传参时采用引用的方式进行传递,函数内部的切片与原始切片底层引用的是同一个数组,函数内对切片的操作会影响到原始切片。但AddStudent函数在添加学生信息到切片后仍需要将追加后的切片返回,并在调用AddStudent函数的地方用原始切片接收返回值,目的是更新原始切片的长度字段和容量字段,否则在遍历切片中的学生信息时仍无法观察到添加的学生信息。
版权归原作者 2021dragon 所有, 如有侵权,请联系我们删除。