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语言操作数据库及其常规操作的示例代码
Apr 21 Golang
go 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
goland 清除所有的默认设置操作
Apr 28 Golang
解决Golang中goroutine执行速度的问题
May 02 Golang
Golang 实现获取当前函数名称和文件行号等操作
May 08 Golang
golang中字符串MD5生成方式总结
Jul 04 Golang
golang实现一个简单的websocket聊天室功能
Oct 05 Golang
Go 中的空白标识符下划线
Mar 25 Golang
简单聊聊Golang中defer预计算参数
Mar 25 Golang
GO语言字符串处理函数之处理Strings包
Apr 14 Golang
Golang 实现 WebSockets 之创建 WebSockets
Apr 24 Golang
GoFrame框架数据校验之校验结果Error接口对象
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
十天学会php之第八天
2006/10/09 PHP
php遍历目录方法小结
2015/03/10 PHP
PHP中trim()函数简单使用指南
2015/04/16 PHP
javascript与CSS复习(《精通javascript》)
2010/06/29 Javascript
SyntaxHighlighter语法高亮插件使用说明
2011/08/14 Javascript
左侧是表头的JS表格控件(自写,网上没有的)
2013/06/04 Javascript
解析dom中的children对象数组元素firstChild,lastChild的使用
2013/07/10 Javascript
js简单实现根据身份证号码识别性别年龄生日
2013/11/29 Javascript
jquery常用特效方法使用示例
2014/04/25 Javascript
js+html5实现canvas绘制简单矩形的方法
2015/06/05 Javascript
关于cookie的初识和运用(js和jq)
2016/04/07 Javascript
jQuery根据name属性进行查找的用法分析
2016/06/23 Javascript
使用plupload自定义参数实现多文件上传
2016/07/19 Javascript
浅谈Emergence.js 检测元素可见性的 js 插件
2017/11/18 Javascript
vue-lazyload图片延迟加载插件的实例讲解
2018/02/09 Javascript
Layui数据表格之获取表格中所有的数据方法
2018/08/20 Javascript
node.js连接mysql与基本用法示例
2019/01/05 Javascript
webpack + vue 打包生成公共配置文件(域名) 方便动态修改
2019/08/29 Javascript
Node.js学习教程之Module模块
2019/09/03 Javascript
浅谈小程序globalData的那些事儿
2019/11/01 Javascript
vue2.0 watch里面的 deep和immediate用法说明
2020/10/30 Javascript
[01:02:05]LGD vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
详细介绍Python语言中的按位运算符
2013/11/26 Python
python简单获取本机计算机名和IP地址的方法
2015/06/03 Python
python微信跳一跳系列之棋子定位像素遍历
2018/02/26 Python
Python爬虫框架Scrapy基本用法入门教程
2018/07/26 Python
Python常用模块之requests模块用法分析
2019/05/15 Python
详解Python中的各种转义符\n\r\t
2019/07/10 Python
Python txt文件如何转换成字典
2020/11/03 Python
python中xlutils库用法浅析
2020/12/29 Python
牵手50香港:专为黄金岁月的单身人士而设的交友网站
2020/08/14 全球购物
我们的节日端午节活动方案
2014/03/02 职场文书
安全生产活动月方案
2014/03/09 职场文书
活动总结模板
2014/05/09 职场文书
电影复兴之路观后感
2015/06/02 职场文书
2016年第32个教师节致辞
2015/11/26 职场文书