Go基础学习之map

Go基础学习之map


前言

最开始学习go语言时,非常粗糙的过了一遍知识点,对很多内容都不了解。为了将基础知识打牢,故此在不断复习的同时回顾整理go语言的基础,诞生了此系列文章。文章为个人学习笔记,如有错误,敬请指正,感激不尽。


以下是本篇文章正文内容,下面案例仅供参考

一、map是什么?

map是一种无序的基于key-value的数据结构,是映射关系容器,其内部使用散列表(hash)实现,属于引用类型,必须初始化才能使用。

1.map的定义

Go语言中 map的定义语法如下:

map[KeyType]ValueType
  • KeyType:表示键的类型。
  • ValueType:表示键对应的值的类型。

map的声明:

var mapVariable map[KeyType]ValueType

map类型的变量默认初始值为nil,如果不初始化 map,那么就会创建一个 nil map。map变量可以和零值比较,但nil map 不能用来存放键值对。所以我们需要使用make()函数来分配内存,语法为:

// 声明后初始化
make(map[KeyType]ValueType, [cap]) 
// 声明的同时初始化
mapVariable := make(map[KeyType]ValueType, [cap]) 

其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量

二、map基本使用

1.置值

map中的数据都是成对出现的:

func main() {
	scoreMap := make(map[string]int, 8)//声明并初始化一个map,其key为string类型,value为int类型
	scoreMap["张三"] = 90              //置值
	scoreMap["李四"] = 95
	scoreMap["李四"] = 100             // 重复增加(key相同),使用新的值覆盖
	fmt.Println(scoreMap)
	fmt.Println(scoreMap["小明"])
	fmt.Printf("type of a:%T\n", scoreMap)	
}

map也支持在声明的时候填充元素,例如:

func main() {
	userInfo := map[string]string{
		"username": "昰阳",
		"password": "123456",
	}
	fmt.Println(userInfo) //map[password:123456 username:昰阳]

注意:

  • 设置值之前必须保证map已经被初始化
  • map的key不能重复,如果重复会进行value,但map的 value 是可以相同的.
  • map的元素不是变量,并不能获取其地址。因为map的增长可能会导致已有元素被重新散列到新的存储位置,这样就可能使得获取的地址无效。
  • 通过下标的的方式访问map中的元素会得到两个值,第二个值是一个布尔值,用来报告该元素是否存在。

2.删除

go语言的delete()内建函数可以帮助我们从map中删除一组键值对:

delete(map, key)
  • map:表示要删除键值对的map
  • key:表示要删除的键值对的键

实例:

func main(){
	userInfo := map[string]string{
		"username": "昰阳",
		"password": "123456",
	}
	userInfo["性别"]="男"
	userInfo["年龄"]="19"
	delete(userInfo, "username")//将username:1昰阳从map中删除
	for k,v := range userInfo{
		fmt.Printf("%v:%v ",k, v)
	}//password:123456 性别:男 年龄:19 
}

3.判断某个key是否存在

前面的注意里我写过,通过下标的的方式访问map中的元素会得到两个值,第二个值是一个布尔值,用来报告该元素是否存在。我们可以利用这个来判断某个key是否存在

判断格式:

value, ok := map[key] //如果key存在,ok为ture value为对应值,不存在key则OK为false,v为值类型的零值```

实例:

func main(){
userInfo := map[string]string{
		"username": "昰阳",
		"password": "123456",
	}
	v, ok := userInfo["username"]
	if ok {
		fmt.Println(v)
	} else {
		fmt.Println("用户不存在")
	}

4.map的遍历

注意:遍历的顺序是随机的。使用for range遍历的时候,k,v使用的同一块内存,这也是容易出现错误的地方

仅遍历key:

func main() {
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 60
	scoreMap["李四"] = 80
	scoreMap["王五"] = 100
	for k := range scoreMap {
		fmt.Println(k)
	}
}

使用for range遍历map:

func main() {
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 60
	scoreMap["李四"] = 80
	scoreMap["王五"] = 100
	for k,v := range scoreMap {
		fmt.Println(k,v)
	}
}

错误举例

由于遍历的时候,遍历v使用的同一块地址,同时这块地址是临时分配的。虽然v的地址没有变化,但v的内容在一直变化,当遍历完成后,v的内容是map遍历时最后遍历的元素的值(map遍历无序,每次不确定哪个元素是最后一个元素)。当程序将v的地址放入到slice中的时候,slice在不断地v的地址插入,由于v一直是那块地址,因此slice中的每个元素记录的都是v的地址。因此当打印slice中的内容的时候,都是同一个值

func main() {
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 60
	scoreMap["李四"] = 80
	scoreMap["王五"] = 100
	var bs []*int
	for k,v := range scoreMap {
		fmt.Printf("k:[%p].v:[%p]\n",&k,&v)// 这里的输出可以看到,k一直使用同一块内存,v也是这个状况
		bs = append(bs,&v)// 对v取了地址
	}
	//将遍历拿到的V的地址输出
	for _,b := range bs{
		fmt.Println(*b)//输出均为三次60/80/100,我试了十几次,大部分是100,但是是可以刷出来80/60的
	}

}

5.按照指定顺序遍历map

func main(){//前提:map是无序的
	rand.Seed(time.Now().UnixNano()) //初始化随机数种子

	var scoreMap = make(map[string]int,200)//初始化一个string:int类型的map,容量为200

	for i:=0;i<100;i++{
		key := fmt.Sprintf("stu%02d",i)//生成wtu开头的字符串
		value:=rand.Intn(100)//随机生成0-99的整数
		scoreMap[key]=value //将生成的字符串以键值对存入scoreMaop
	}
	for k:=range scoreMap{
		fmt.Println(k,scoreMap[k])
	}
	//取出map中的所有key存入切片keys
	var keys=make([]string,0,200)
	for k,_:=range scoreMap{
		keys=append(keys, k)
	}
	//对切片进行排序
	sort.Strings(keys)
	//按照排序后的key遍历map
	for _,key:=range keys{
		fmt.Println(key,scoreMap[key])
	}
}

另外:go语言的冷len()函数可以获取map键值对个数

len(map)

三、更多

1.元素为map类型的切片

func main() {
	var mapSlice = make([]map[string]string, 3)
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
	fmt.Println("after init")
	// 对切片中的map元素进行初始化
	mapSlice[0] = make(map[string]string, 10)
	mapSlice[0]["name"] = "昰阳"
	mapSlice[0]["password"] = "123456"
	mapSlice[0]["address"] = "火星"
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
}

2.值为切片类型的map

func main() {
    var sliceMap = make(map[string][]string, 3)
    fmt.Println(sliceMap)
    fmt.Println("after init")
    key := "中国"
    value, ok := sliceMap[key]
    if !ok {
        value = make([]string, 0, 2)
    }
    value = append(value, "北京", "上海")
    sliceMap[key] = value
    fmt.Println(sliceMap)
}

总结

本文简述了一些关于map的定义和常用的使用方法,map的底层存储方式为数组,后面如果有时间我会在更新一下笔记,写一写我对map实现原理的理解,其实这部分内容在中文文档上已经写得很清楚了,有兴趣的培养可以去看看
本文参考文章:
go中文文档:http://www.topgoer.com/
https://www.liwenzhou.com/
https://blog.csdn.net/qq_29695701/article/details/93167352
https://www.cnblogs.com/bingzhen/p/10503967.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昰阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值