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:垃圾回收GC触发条件详解
Apr 24 Golang
Go语言使用select{}阻塞main函数介绍
Apr 25 Golang
golang正则之命名分组方式
Apr 25 Golang
golang中的空slice案例
Apr 27 Golang
浅谈Golang 嵌套 interface 的赋值问题
Apr 29 Golang
go语言基础 seek光标位置os包的使用
May 09 Golang
golang fmt格式“占位符”的实例用法详解
Jul 04 Golang
Golang表示枚举类型的详细讲解
Sep 04 Golang
Go语言grpc和protobuf
Apr 13 Golang
详解Go语言中配置文件使用与日志配置
Jun 01 Golang
Go gorilla securecookie库的安装使用详解
Aug 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
建立文件交换功能的脚本(二)
2006/10/09 PHP
转换中文日期的PHP程序
2006/10/09 PHP
PHP简介
2006/10/09 PHP
php为什么选mysql作为数据库? Mysql 创建用户方法
2007/07/02 PHP
PHP实现多图上传和单图上传功能
2018/05/17 PHP
仿百度输入框智能提示的js代码
2013/08/22 Javascript
jquery ajax jsonp跨域调用实例代码
2013/12/11 Javascript
JQuery打造省市下拉框联动效果
2014/05/18 Javascript
Html5的placeholder属性(IE兼容)实现代码
2014/08/30 Javascript
了解Javascript的模块化开发
2015/03/02 Javascript
JS数组array元素的添加和删除方法代码实例
2015/06/01 Javascript
jQuery Pagination分页插件使用方法详解
2017/02/28 Javascript
Vue.js学习教程之列表渲染详解
2017/05/17 Javascript
Kotlin学习第一步 kotlin语法特性
2017/05/25 Javascript
不到200行 JavaScript 代码实现富文本编辑器的方法
2018/01/03 Javascript
JavaScript实现创建自定义对象的常用方式总结
2018/07/09 Javascript
微信小程序实现同时上传多张图片
2020/02/03 Javascript
微信小程序实现手指拖动选项排序
2020/04/22 Javascript
[49:08]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第一场 11.27
2020/12/01 DOTA
python2.7的编码问题与解决方法
2016/10/04 Python
python批量修改文件夹及其子文件夹下的文件内容
2019/03/15 Python
Python 列表去重去除空字符的例子
2019/07/20 Python
python实现的生成word文档功能示例
2019/08/23 Python
用sleep间隔进行python反爬虫的实例讲解
2020/11/30 Python
使用pandas实现筛选出指定列值所对应的行
2020/12/13 Python
老生常谈CSS中的长度单位
2016/06/27 HTML / CSS
使用HTML5的File实现base64和图片的互转
2013/08/01 HTML / CSS
英国曼彻斯特宠物用品品牌:Bunty Pet Products
2019/07/27 全球购物
老师给学生的表扬信
2014/01/17 职场文书
总经理岗位职责描述
2014/02/08 职场文书
社区党员志愿服务活动方案
2014/08/18 职场文书
2014年远程教育工作总结
2014/12/09 职场文书
幼儿园辞职信范文
2015/02/27 职场文书
综治目标管理责任书
2015/05/11 职场文书
先进基层党组织主要事迹材料
2015/11/03 职场文书
nginx反向代理配置去除前缀案例教程
2021/07/26 Servers