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 相关文章推荐
golang如何去除多余空白字符(含制表符)
Apr 25 Golang
Go语言带缓冲的通道实现
Apr 26 Golang
golang 实现并发求和
May 08 Golang
go语言基础 seek光标位置os包的使用
May 09 Golang
go 实现简易端口扫描的示例
May 22 Golang
Golang中异常处理机制详解
Jun 08 Golang
go web 预防跨站脚本的实现方式
Jun 11 Golang
Go语言读取txt文档的操作方法
Jan 22 Golang
golang实现浏览器导出excel文件功能
Mar 25 Golang
Go并发4种方法简明讲解
Apr 06 Golang
golang三种设计模式之简单工厂、方法工厂和抽象工厂
Apr 10 Golang
golang操作redis的客户端包有多个比如redigo、go-redis
Apr 14 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
thinkphp3.0 模板中函数的使用
2012/11/13 PHP
php按单词截取字符串的方法
2015/04/07 PHP
PHP从零开始打造自己的MVC框架之入口文件实现方法详解
2019/06/03 PHP
php实现登录页面的简单实例
2019/09/29 PHP
JS学习之一个简易的日历控件
2010/03/24 Javascript
Javascript的getYear、getFullYear、getUTCFullYear异同分享
2011/11/30 Javascript
基于jquery编写的横向自适应幻灯片切换特效的实例代码
2013/08/06 Javascript
点击弹出层外区域关闭弹出层jquery特效示例
2013/08/25 Javascript
使用insertAfter()方法在现有元素后添加一个新元素
2014/05/28 Javascript
javascript进行数组追加方法小结
2014/06/16 Javascript
JS获取时间的方法
2015/01/21 Javascript
原生node.js案例--前后台交互
2017/02/20 Javascript
vue.js中mint-ui框架的使用方法
2017/05/12 Javascript
详解如何构建Angular项目目录结构
2017/07/13 Javascript
jQuery 实现鼠标画框并对框内数据选中的实例代码
2017/08/29 jQuery
Angularjs之ngModel中的值验证绑定方法
2018/09/13 Javascript
微信小程序搜索框样式并实现跳转到搜索页面(小程序搜索功能)
2020/03/10 Javascript
封装 axios+promise通用请求函数操作
2020/08/11 Javascript
详解vue修改elementUI的分页组件视图没更新问题
2020/11/13 Javascript
[02:27]2018DOTA2亚洲邀请赛赛前采访-OpTic
2018/04/03 DOTA
Python性能优化的20条建议
2014/10/25 Python
Python使用struct处理二进制的实例详解
2017/09/11 Python
python 删除列表里所有空格项的方法总结
2018/04/18 Python
对python sklearn one-hot编码详解
2018/07/10 Python
使用Python3内置文档高效学习以及官方中文文档
2019/05/19 Python
ubuntu 16.04下python版本切换的方法
2019/06/14 Python
Python跳出多重循环的方法示例
2019/07/03 Python
Django 开发调试工具 Django-debug-toolbar使用详解
2019/07/23 Python
python绘制随机网络图形示例
2019/11/21 Python
HTML5新增属性data-*和js/jquery之间的交互及注意事项
2017/08/08 HTML / CSS
复古风格的女装和装饰品:ModCloth
2017/12/29 全球购物
联想西班牙官网:Lenovo西班牙
2018/08/28 全球购物
西班牙土拨鼠床垫公司,感觉在云端:Marmota
2019/03/18 全球购物
什么叫做SQL注入,如何防止
2016/10/04 面试题
nohup的用法
2014/08/10 面试题
决心书范文
2014/03/11 职场文书