Golang 空map和未初始化map的注意事项说明


Posted in Golang onApril 29, 2021

可以对未初始化的map进行取值,但取出来的东西是空:

var m1 map[string]string
fmt.Println(m1["1"])

不能对未初始化的map进行赋值,这样将会抛出一个异常:

panic: assignment to entry in nil map

var m1 map[string]string
m1["1"] = "1"

通过fmt打印map时,空map和nil map结果是一样的,都为map[]。所以,这个时候别断定map是空还是nil,而应该通过map == nil来判断。

补充:Golang清空map的两种方式及性能比拼

一、Golang中删除map的方法

1、所有Go版本通用方法

a := make(map[string]int)
a["a"] = 1
a["b"] = 2
// clear all
a = make(map[string]int)

2. Go 1.11版本以上用法

通过Go的内部函数mapclear方法删除。这个函数并没有显示的调用方法,当你使用for循环遍历删除所有元素时,Go的编译器会优化成Go内部函数mapclear。

package main
func main() {
        m := make(map[byte]int)
        m[1] = 1
        m[2] = 2
        for k := range m {
	        delete(m, k)
        }
}

把上述源代码直接编译成汇编(默认编译是会优化的):

go tool compile -S map_clear.go

可以看到编译器把源码9行的for循环直接优化成了mapclear去删除所有元素。如下:

Golang 空map和未初始化map的注意事项说明

再来看看关闭优化后的结果:

go tool compile -l -N -S map_clear.go

关闭优化选项后,Go编译器直接通过循环遍历来删除map里面的元素。

Golang 空map和未初始化map的注意事项说明

具体的mapclear代码可以在go源码库中runtime/map.go文件中看到,代码如下:

// mapclear deletes all keys from a map.
func mapclear(t *maptype, h *hmap) {
	if raceenabled && h != nil {
		callerpc := getcallerpc()
		pc := funcPC(mapclear)
		racewritepc(unsafe.Pointer(h), callerpc, pc)
	}
	if h == nil || h.count == 0 {
		return
	}
	if h.flags&hashWriting != 0 {
		throw("concurrent map writes")
	}
	h.flags ^= hashWriting
	h.flags &^= sameSizeGrow
	h.oldbuckets = nil
	h.nevacuate = 0
	h.noverflow = 0
	h.count = 0
	// Keep the mapextra allocation but clear any extra information.
	if h.extra != nil {
		*h.extra = mapextra{}
	}
	// makeBucketArray clears the memory pointed to by h.buckets
	// and recovers any overflow buckets by generating them
	// as if h.buckets was newly alloced.
	_, nextOverflow := makeBucketArray(t, h.B, h.buckets)
	if nextOverflow != nil {
		// If overflow buckets are created then h.extra
		// will have been allocated during initial bucket creation.
		h.extra.nextOverflow = nextOverflow
	}
	if h.flags&hashWriting == 0 {
		throw("concurrent map writes")
	}
	h.flags &^= hashWriting
}

二、两种清空map方式性能比较

1、先用benchmark的方式测一下两种方式

benchmark代码如下:

func BenchmarkMakeNewMap(b *testing.B) {
	tmpMap := make(map[string]string, 10000)
	for i := 0; i < b.N; i++ {
		for j := 0; j < 10000; j++ {
			tmpMap["tmp"+strconv.Itoa(j)] = "tmp"
		}
		tmpMap = make(map[string]string, 10000)
	}
}
func BenchmarkDeleteMap(b *testing.B) {
	tmpMap := make(map[string]string, 10000)
	for i := 0; i < b.N; i++ {
		for j := 0; j < 10000; j++ {
			tmpMap["tmp"+strconv.Itoa(j)] = "tmp"
		}
		for k := range tmpMap {
			delete(tmpMap, k)
		}
	}
}

得到测试结果如下:

Golang 空map和未初始化map的注意事项说明

从测试结果上看,好像确实delete的方式效率更高,但是这个benchmark中总感觉没有测试到真正清空map的地方,中间穿插着put map的操作,我们用方法2再测一下。

2、单个UT测一下两种方式

UT代码如下:

测试过程中禁用了gc,避免gc对运行时间和内存产生干扰。

func TestMakeNewMap(t *testing.T) {
   debug.SetGCPercent(-1)
   var m runtime.MemStats
   tmpMap := make(map[string]string, 1000000)
   for j := 0; j < 1000000; j++ {
      tmpMap["tmp"+strconv.Itoa(j)] = "tmp"
   }
   start := time.Now()
   tmpMap = make(map[string]string, 1000000)
   fmt.Println(time.Since(start).Microseconds())
   runtime.ReadMemStats(&m)
   fmt.Printf("%d Kb\n", m.Alloc/1024)
}
func TestDeleteMap(t *testing.T) {
   debug.SetGCPercent(-1)
   var m runtime.MemStats
   tmpMap2 := make(map[string]string, 1000000)
   for j := 0; j < 1000000; j++ {
      tmpMap2["tmp"+strconv.Itoa(j)] = "tmp"
   }
   start := time.Now()
   for k := range tmpMap2 {
      delete(tmpMap2, k)
   }
   fmt.Println(time.Since(start).Microseconds())
   runtime.ReadMemStats(&m)
   fmt.Printf("%d Kb\n", m.Alloc/1024)
}

测试结果如下:

Golang 空map和未初始化map的注意事项说明

从测试结果上看,好像确实是make方式的效率更低,而且内存占用更多,但结果真的是这样吗?

我们把make方式的make map的大小改为0再试一下:

tmpMap = make(map[string]string)

得到如下结果,What?时间为0了,内存消耗也跟delete的方式一样:

Golang 空map和未初始化map的注意事项说明

我们把make方式的make map的大小改为10000再试一下:

tmpMap = make(map[string]string, 10000)

结果如下:

Golang 空map和未初始化map的注意事项说明

三、总结

通过上面的测试,可以得出结论:

1、在map的数量级在10w以内的话,make方式会比delete方式速度更快,但是内存会消耗更多一点。

2、如果map数量级大于10w的话,delete的速度会更快,且内存消耗更少。

3、对于不再使用的map,直接使用make方式,长度为0清空更快。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
go语言中切片与内存复制 memcpy 的实现操作
Apr 27 Golang
Go语言切片前或中间插入项与内置copy()函数详解
Apr 27 Golang
goland 设置project gopath的操作
May 06 Golang
Go 自定义package包设置与导入操作
May 06 Golang
Golang 实现获取当前函数名称和文件行号等操作
May 08 Golang
go语言中http超时引发的事故解决
Jun 02 Golang
Golang的继承模拟实例
Jun 30 Golang
go开发alertmanger实现钉钉报警
Jul 16 Golang
Go语言安装并操作redis的go-redis库
Apr 14 Golang
Golang 链表的学习和使用
Apr 19 Golang
Go gRPC进阶教程gRPC转换HTTP
Jun 16 Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
Jun 21 Golang
彻底理解golang中什么是nil
基于Go Int转string几种方式性能测试
Apr 28 #Golang
Go语言中break label与goto label的区别
golang 如何用反射reflect操作结构体
Apr 28 #Golang
golang 生成对应的数据表struct定义操作
Apr 28 #Golang
golang 如何通过反射创建新对象
Apr 28 #Golang
golang 实现两个结构体复制字段
Apr 28 #Golang
You might like
对盗链说再见...
2006/10/09 PHP
PHP静态类
2006/11/25 PHP
PHP笔记之:基于面向对象设计的详解
2013/05/14 PHP
PHP开发框架kohana中处理ajax请求的例子
2014/07/14 PHP
php在线解压ZIP文件的方法
2014/12/30 PHP
php生成图片验证码的方法
2016/04/15 PHP
js 发个判断字符串是否为符合标准的函数
2009/04/27 Javascript
简单几行JS Code实现IE邮件转发新浪微博
2013/07/03 Javascript
JavaScript 模拟类机制及私有变量的方法及思路
2013/07/10 Javascript
公共js在页面底部加载的注意事项介绍
2013/07/18 Javascript
JQuery打造省市下拉框联动效果
2014/05/18 Javascript
AngularJS基础 ng-paste 指令简单示例
2016/08/02 Javascript
JavaScript 高性能数组去重的方法
2018/09/20 Javascript
jQuery Ajax实现Select多级关联动态绑定数据的实例代码
2018/10/26 jQuery
Layui 动态禁止select下拉的例子
2019/09/03 Javascript
解决layui调用自定义方法提示未定义的问题
2019/09/14 Javascript
在Vue中使用this.$store或者是$route一直报错的解决
2019/11/08 Javascript
[03:09]2014DOTA2国际邀请赛 赛场上的美丽风景线 中国Coser也爱DOTA2
2014/07/20 DOTA
python实现域名系统(DNS)正向查询的方法
2016/04/19 Python
Windows下Eclipse+PyDev配置Python+PyQt4开发环境
2016/05/17 Python
利用Pandas 创建空的DataFrame方法
2018/04/08 Python
Java与Python两大幸存者谁更胜一筹呢
2018/04/12 Python
python实现的汉诺塔算法示例
2019/10/23 Python
Python 实现平台类游戏添加跳跃功能
2020/03/27 Python
Python如何获取文件指定行的内容
2020/05/27 Python
Python pandas对excel的操作实现示例
2020/07/21 Python
paramiko使用tail实时获取服务器的日志输出详解
2020/12/06 Python
澳大利亚吉他在线:Artist Guitars
2017/03/30 全球购物
面临毕业的毕业生自荐书范文
2014/02/05 职场文书
《小壁虎借尾巴》教学反思
2014/02/16 职场文书
《在山的那边》教学反思
2014/02/23 职场文书
小学红领巾广播稿(3篇)
2014/09/13 职场文书
2014年社区重阳节活动策划方案
2014/09/16 职场文书
优秀共青团员事迹材料
2014/12/25 职场文书
学习《中小学教师职业道德规范》心得体会
2016/01/18 职场文书
品牌形象定位,全面分析
2019/07/23 职场文书