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

可以对未初始化的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去删除所有元素。如下:

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

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

关闭优化选项后,Go编译器直接通过循环遍历来删除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)
		}
	}
}

得到测试结果如下:

从测试结果上看,好像确实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)
}

测试结果如下:

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

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

tmpMap = make(map[string]string)

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

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

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

结果如下:

三、总结

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

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

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

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

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

(0)

相关推荐

  • Golang自定义结构体转map的操作

    在Golang中,如何将一个结构体转成map? 本文介绍两种方法.第一种是是使用json包解析解码编码.第二种是使用反射,使用反射的效率比较高,代码在这里.如果觉得代码有用,可以给我的代码仓库一个star. 假设有下面的一个结构体 func newUser() User { name := "user" MyGithub := GithubPage{ URL: "https://github.com/liangyaopei", Star: 1, } NoDive :

  • 快速解决Golang Map 并发读写安全的问题

    一.错误案例 package main import ( "fmt" "time" ) var TestMap map[string]string func init() { TestMap = make(map[string]string, 1) } func main() { for i := 0; i < 1000; i++ { go Write("aaa") go Read("aaa") go Write(&qu

  • 解决Golang map range遍历结果不稳定问题

    闲言少叙,本文主要是想介绍一个Golang开发常见的一个问题.然而,此问题对于初学者来说却经常容易陷入坑中. 问题 我在写一段代码时,使用了Golang的map数据结构,目的是想用map缓存计数结果.简单来说map的键也是整型的,且以递增顺序存储.我的最初想法是,在统计结束后,按照map中存储的键有序输出值.可是,当我运行程序时,结果并不是我想要的,而且有一定概率运行结果不同. 问题代码 func sortByBits(arr []int) []int { var bitmap = make(m

  • golang 如何获取map所有key的方式

    最佳方式:根据map的长度,新建一个数组,遍历map逐个压入 方法1(效率很高): func getKeys1(m map[int]int) []int { // 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高 j := 0 keys := make([]int, len(m)) for k := range m { keys[j] = k j++ } return keys } 方法2(效率很高): func getKeys2(m map[int]int) [

  • golang判断key是否在map中的代码

    个人常用函数 func IsContain(items []string, item string) bool { for _, eachItem := range items { if eachItem == item { return true } } return false } 使用方法 var word := "my" var sentence := []string{"my","word","in","a

  • golang 实现对Map进行键值自定义排序

    看代码吧~ //自定义结构体,用来自定义排序 type RecentlyUpdateListMapSorter []map[string]interface{} //根据MapSorter.Keys进行倒序排序 func (ms *RecentlyUpdateListMapSorter) SortReverse() { sort.Sort(sort.Reverse(ms)) } //自定义排序方法(Len(),Less(),Swap()) func (ms *RecentlyUpdateList

  • golang 实现struct、json、map互相转化

    一.Json和struct互换 (1)Json转struct例子: package main import ( "fmt" "encoding/json" ) type People struct { Name string `json:"name_title"` Age int `json:"age_size"` } func JsonToStructDemo(){ jsonStr := ` { "name_tit

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

    可以对未初始化的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[].所以,这个时候别

  • 关于List、Map、Stream初始化方式

    目录 List.Map.Stream初始化 第一种方法 第二种方法(双括号初始化法/匿名内部类) 有内存泄露隐患 List Stream 常用方法 声明Student对象 Stream一些常用的API List.Map.Stream初始化 第一种方法      初始化List      List<string> list = new ArrayList<string><string>();        list.add("string1");  

  • golang中range在slice和map遍历中的注意事项

    golang中range在slice和map遍历中的注意事项 package main import ( "fmt" ) func main() { slice := []int{0, 1, 2, 3} myMap := make(map[int]*int) for _,v :=range slice{ if v==1 { v=100 } } for k,v :=range slice{ fmt.Println("k:",k,"v:",v) }

  • java中Map和List初始化的N种方法总结

    目录 Map和List初始化方法 第一种方法(常用方法) 第二种方法(双括号初始化法) 第三种,stream初始化 初始化Map和List小技巧(指定容量+匿名内部类初始化) 总结 Map和List初始化方法 第一种方法(常用方法) //初始化List   List<string> list = new ArrayList<string><string>();   list.add("string1");   list.add("strin

  • Golang空结构体struct{}用途,你知道吗

    golang 空结构体 struct{} 可以用来节省内存 a := struct{}{} println(unsafe.Sizeof(a)) // Output: 0 理由如下: 如果使用的是map,而且map又很长,通常会节省不少资源 空struct{}也在向别人表明,这里并不需要一个值 本例说明在map里节省资源的用途: set := make(map[string]struct{}) for _, value := range []string{"apple", "o

  • 浅谈Java之Map 按值排序 (Map sort by value)

    Map是键值对的集合,又叫作字典或关联数组等,是最常见的数据结构之一.在java如何让一个map按value排序呢? 看似简单,但却不容易! 比如,Map中key是String类型,表示一个单词,而value是int型,表示该单词出现的次数,现在我们想要按照单词出现的次数来排序: Map map = new TreeMap(); map.put("me", 1000); map.put("and", 4000); map.put("you", 3

  • Map获取键值,Map的几种遍历方法总结(推荐)

    Map获取键值 Map以按键/数值对的形式存储数据,和数组非常相似,在数组中存在的索引,它们本身也是对象. Map的接口 Map---实现Map Map.Entry--Map的内部类,描述Map中的按键/数值对. SortedMap---扩展Map,使按键保持升序排列 关于怎么使用,一般是选择Map的子类,而不直接用Map类. 下面以HashMap为例 public static void main(String args[]) { HashMap hashmap =new HashMap();

  • 详解Python map函数及Python map()函数的用法

    python map函数 map()函数 map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回. 例如,对于list [1, 2, 3, 4, 5, 6, 7, 8, 9] 如果希望把list的每个元素都作平方,就可以用map()函数: 因此,我们只需要传入函数f(x)=x*x,就可以利用map()函数完成这个计算: def f(x): return x*x print map(f,

  • 对python内置map和six.moves.map的区别详解

    python内置map返回的是列表,而six.moves.map返回的是iter. >>> map(lambda a: a*2, [1, 2, 3]) [2, 4, 6] >>> m = six.moves.map(lambda a: a*2, [1, 2, 3]) >>> type(m) <type 'itertools.imap'> >>> next(m) 2 >>> next(m) 4 >&g

随机推荐