Go 学习笔记(12)— 字典map定义、初始化、读取字典、删除字典、清空字典、map 按 key 进行有序遍历

Go 中字典也叫做 mapmap 是一种无序的键值对的集合。 map 最重要的一点是通过 key 来快速检索数据, key 类似于索引,指向数据的值。

1. 字典定义

可以使用内建函数 make 也可以使用 map 关键字来定义 map:

/* 声明变量,默认 map 是 nil */
var mapName map[mapKey]mapValue

/* 使用 make 函数 */
mapName := make(map[mapKey]mapValue)

这两者区别在:

  • 直接用 var 声明,只是声明了变量 mapName,但是没有初始化内存,如果直接写入会导致出现 nil map 继而导致恐慌(panic)事;

  • 使用内置函数 make,会声明并初始化一个哈希映射数据结构,并返回一个指向它的映射值。这是正确的初始化 map 的方法。

定义一个字典类型变量语法格式有以下多种写法:

var mapName  map[mapKey]dataType
var mapName  map[mapKey]dataType = make(map[mapKey]dataType) 
var mapName  = make(map[mapKey]dataType) 
var mapName  map[mapKey]dataType = map[mapKey]dataType{}
var mapName  = map[mapKey]dataType{}
mapName  := map[mapKey]dataType{}
mapName  := make(map[mapKey]dataType)
// 创建一个映射,键的类型是string,值的类型是int
dict := make(map[string]int) // map 容量使用默认值
dict := make(map[string]int, len) // map 容量使用给定的 len 值

// 创建一个映射,键和值的类型都是string
// 使用两个键值对初始化映射
dict := map[string]string{"country": "China", "province": "Shaanxi"}

2. 字典初始化

如果不初始化 map ,那么就会创建一个 nil mapnil map 不能用来存放键值对。

可以通过声明一个未初始化的映射来创建一个值为 nil 的映射(称为 nil 映射)。

nil 映射不能用于存储键值对,否则,会产生一个语言运行时错误,如下

// 通过声明映射创建一个nil映射
var book map[string]string
book["author"] = "wohu"

Runtime Error:
panic: runtime error: assignment to entry in nil map

但是使用内置函数 make 创建的 map 可以用于存储键值对。

book := make(map[string]string)
book["author"] = "wohu"
fmt.Printf("book is %v\n", book) // book is map[author:wohu]
fmt.Printf("book is %v\n", book["author"]) // book is wohu

或者使用如下赋值

d1 := make(map[string]string)
var d2 = map[string]string{} // 注意:后面带了个大括号
d1["age"] = "18"
d2["name"] = "wohu"
fmt.Printf("d1 is %v\n", d1)	// d1 is map[age:18]
fmt.Printf("d2 is %v\n", d2)	// d2 is map[name:wohu]

使用函数 len() 可以获取 map 中 pair 的数目。

3. 字典键类型约束

Go 语言字典的键类型不可以是函数类型、字典类型和切片类型。

Go 语言规范规定,在键类型的值之间必须可以施加操作符 ==!= 。换句话说,键类型的值必须要支持判等操作。由于函数类型、字典类型和切片类型的值并不支持判等操作,所以字典的键类型不能是这些类型。

Go 语言中要求,key 的类型必须支持 ==!= 两种比较操作符。而函数类型、map 类型自身,以及切片只支持与 nil 的比较,而不支持同类型两个变量的比较。


s1 := make([]int, 1)
s2 := make([]int, 2)
f1 := func() {}
f2 := func() {}
m1 := make(map[int]string)
m2 := make(map[int]string)
println(s1 == s2) // 错误:invalid operation: s1 == s2 (slice can only be compared to nil)
println(f1 == f2) // 错误:invalid operation: f1 == f2 (func can only be compared to nil)
println(m1 == m2) // 错误:invalid operation: m1 == m2 (map can only be compared to nil)

所以函数类型、map 类型自身,以及切片类型是不能作为 mapkey 类型的。

另外,如果键的类型是接口类型的,那么键值的实际类型也不能是上述三种类型,否则在程序运行过程中会引发 panic(即运行时恐慌)。

4. 遍历字典

注意:遍历输出元素的顺序与填充顺序无关,不能期望 map 在遍历时返回某种期望顺序的结果。

package main

import "fmt"

func main() {
	book := make(map[string]string)

	book["price"] = "100"
	book["author"] = "wohu"
	book["language"] = "Chinese"

	for k := range book { // 迭代 key, 不能保证每次迭代元素的顺序
		fmt.Println(k, "value is ", book[k])
	}

	for k, v := range book { // 同时迭代 key 和 value
		fmt.Println(k, v)
	}

	/*查看元素在集合中是否存在 */
	published, ok := book["published"] /*如果确定是真实的,则存在,否则不存在 */
	/*fmt.Println(published) */
	/*fmt.Println(ok) */
	if ok {
		fmt.Println("published is ", published)
	} else {
		fmt.Println("published not exist")
	}
}

运行结果为:

price value is  100
author value is  wohu
language value is  Chinese
price 100
author wohu
language Chinese
published not exist

读取字典,就是根据 key 值,在字典中查找 key 对应的 value 值,读取字典通常有两种方法,分别是:

// 第一种情况,断言查询,推荐使用这种方法
val,ok := map[key]

// 第二种情况直接查询
val := map[key]

上边两种读取字典的方法中,

  • 第一种采用断言的方式,当 oktrue 时,表示 key 存在于字典中,当 okfalse 时,表示 key 不存在于字典中;
  • 第二种情况直接查询 key 值的方式,如果 key 不在 map 种时,返回值是字典定义中 value 的类型默认初始值。如 value 的类型为 int,则默认值为 0,如果 value 类型是指针,则默认是 nil

5. 删除 map 元素

Go 语言提供了一个内置函数 delete() ,用于删除容器内的元素,使用 delete() 内建函数从 map 中删除一组键值对, delete() 函数的格式如下:

delete(map, key)

其中 map 为要删除的 map 实例, key 为要删除的 map 中键值对的键。

package main

import "fmt"

func main() {
	book := map[string]string{
		"price":    "100",
		"author":   "wohu",
		"language": "Chinese", // 数组或者字典在多行编写时必须要在最后一个元素后面加逗号
	}
// syntax error: unexpected newline, expecting comma or }
	for k, v := range book {
		fmt.Println("k is ", k, ",v is ", v)
	}

	delete(book, "author")
	for k, v := range book {
		fmt.Println("k is ", k, ",v is ", v)
	}

	mapLength := len(book)
	fmt.Println("book length is ", mapLength)
}

delete 函数是从 map 中删除键的唯一方法。即便传给 delete 的键在 map 中并不存在,delete 函数的执行也不会失败,更不会抛出运行时的异常。

6. 清空 map

Go 语言中并没有为 map 提供任何清空所有元素的函数、方法,清空 map 的唯一办法就是重新 make 一个新的 map ,不用担心垃圾回收的效率, Go 语言中的并行垃圾回收效率比写一个清空函数要高效的多。

7. 用切片作为 map 的值

既然一个 key 只能对应一个 value ,而 value 又是一个原始类型,那么如果一个 key 要对应多个值怎么办?

通过将 value 定义为 []int 类型或者其他类型的切片,就可以优雅的解决这个问题,示例代码如下所示:

mp1 := make(map[int][]int)
mp2 := make(map[int]*[]int)

8. map 的有序遍历

8.1 先对key进行排序,再根据 key 进行遍历

  • Go 中没有一个专门的方法针对 mapkey 进行排序
  • Go 中的 map 默认是无序的,注意也不是按照添加的顺序存放的,你每次遍历,得到的输出可能不一样
  • Gomap 的排序,是先将 key 进行排序,然后根据 key 值遍历输出即可
package main

import (
	"fmt"
	"sort"
)

func main() {
	//1.定义一个key打乱的字典
	person := map[int]string{3: "张学友", 1: "刘德华", 2: "郭富城", 4: "黎明", 5: "我"}

	//2.定义一个切片
	s := make([]int, 0, len(person))

	//3.遍历map获取key-->s1中
	for key := range person {
		s = append(s, key)
	}

	//4.给s进行排序
	sort.Ints(s)
	// sort.Strings(a)

	//5. 遍历s 来读取 person
	for _, k := range s { // 先下标,再数值
		fmt.Println(k, person[k])
	}
}

输出:

1 刘德华
2 郭富城
3 张学友
4 黎明
5

8.2 使用 url.Values{} 进行排序

url.Values{} 引用 Go标准库 http/url,查看库,可以看到 url.Values 的定义如下:

type Values map[string][]string

参见:https://blog.csdn.net/wohu1104/article/details/106629308

从定义可以看出,本质上 map 的扩展方法。我们常用的有 SetAddDelEncode 等函数。

package main

import (
	"fmt"
	"net/url"
)

func main() {
	v := url.Values{}
	v.Add("1", "一")
	v.Add("2", "二")
	v.Add("3", "三")
	v.Add("4", "四")
	v.Add("5", "五")

	fmt.Printf("%#v", v)
}

9. map 变量的传递开销

和切片类型一样,map 也是引用类型。这就意味着 map 类型变量作为参数被传递给函数或方法的时候,实质上传递的只是一个“描述符”,而不是整个 map 的数据拷贝,所以这个传递的开销是固定的,而且也很小。

并且,当 map 变量被传递到函数或方法内部后,我们在函数内部对 map 类型参数的修改在函数外部也是可见的。


package main
  
import "fmt"

func foo(m map[string]int) {
    m["key1"] = 11
    m["key2"] = 12
}

func main() {
    m := map[string]int{
        "key1": 1,
        "key2": 2,
    }

    fmt.Println(m) // map[key1:1 key2:2]  
    foo(m)
    fmt.Println(m) // map[key1:11 key2:12] 
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值