可以對未初始化的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結(jié)果是一樣的,都為map[]。所以,這個時候別斷定map是空還是nil,而應(yīng)該通過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的內(nèi)部函數(shù)mapclear方法刪除。這個函數(shù)并沒有顯示的調(diào)用方法,當(dāng)你使用for循環(huán)遍歷刪除所有元素時,Go的編譯器會優(yōu)化成Go內(nèi)部函數(shù)mapclear。
package main
func main() {
m := make(map[byte]int)
m[1] = 1
m[2] = 2
for k := range m {
delete(m, k)
}
}
把上述源代碼直接編譯成匯編(默認編譯是會優(yōu)化的):
go tool compile -S map_clear.go
可以看到編譯器把源碼9行的for循環(huán)直接優(yōu)化成了mapclear去刪除所有元素。如下:
再來看看關(guān)閉優(yōu)化后的結(jié)果:
go tool compile -l -N -S map_clear.go
關(guān)閉優(yōu)化選項后,Go編譯器直接通過循環(huán)遍歷來刪除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.flagshashWriting != 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.flagshashWriting == 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)
}
}
}
得到測試結(jié)果如下:
從測試結(jié)果上看,好像確實delete的方式效率更高,但是這個benchmark中總感覺沒有測試到真正清空map的地方,中間穿插著put map的操作,我們用方法2再測一下。
2、單個UT測一下兩種方式
UT代碼如下:
測試過程中禁用了gc,避免gc對運行時間和內(nèi)存產(chǎn)生干擾。
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)
}
測試結(jié)果如下:
從測試結(jié)果上看,好像確實是make方式的效率更低,而且內(nèi)存占用更多,但結(jié)果真的是這樣嗎?
我們把make方式的make map的大小改為0再試一下:
tmpMap = make(map[string]string)
得到如下結(jié)果,What?時間為0了,內(nèi)存消耗也跟delete的方式一樣:
我們把make方式的make map的大小改為10000再試一下:
tmpMap = make(map[string]string, 10000)
結(jié)果如下:
三、總結(jié)
通過上面的測試,可以得出結(jié)論:
1、在map的數(shù)量級在10w以內(nèi)的話,make方式會比delete方式速度更快,但是內(nèi)存會消耗更多一點。
2、如果map數(shù)量級大于10w的話,delete的速度會更快,且內(nèi)存消耗更少。
3、對于不再使用的map,直接使用make方式,長度為0清空更快。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
您可能感興趣的文章:- golang 實現(xiàn)對Map進行鍵值自定義排序
- golang 如何獲取map所有key的方式
- golang判斷key是否在map中的代碼
- 解決Golang map range遍歷結(jié)果不穩(wěn)定問題
- 快速解決Golang Map 并發(fā)讀寫安全的問題
- golang 實現(xiàn)struct、json、map互相轉(zhuǎn)化
- Golang自定義結(jié)構(gòu)體轉(zhuǎn)map的操作
- Golang 使用Map實現(xiàn)去重與set的功能操作