Go語言map實現順序讀取
Go 語言中的 map 是一種非常強大的數據結構,它允許我們快速地存儲和檢索鍵值對。
然而,當我們遍歷 map 時,會有一個有趣的現象,那就是輸出的鍵值對順序是不確定的。
現象
先看一段代碼示例:
package main import "fmt" func main() { ? ? m := map[string]int{ ? ? ? ? "apple": ?1, ? ? ? ? "banana": 2, ? ? ? ? "orange": 3, ? ? } ? ? for k, v := range m { ? ? ? ? fmt.Printf("key=%s, value=%d\n", k, v) ? ? } }
當我們多執(zhí)行幾次這段代碼時,就會發(fā)現,輸出的順序是不同的。
原因
首先,Go 語言 map 的底層實現是哈希表,在進行插入時,會對 key 進行 hash 運算。這也就導致了數據不是按順序存儲的,和遍歷的順序也就會不一致。
第二,map 在擴容后,會發(fā)生 key 的搬遷,原來落在同一個 bucket 中的 key,搬遷后,有些 key 可能就到其他 bucket 了。
而遍歷的過程,就是按順序遍歷 bucket,同時按順序遍歷 bucket 中的 key。
搬遷后,key 的位置發(fā)生了重大的變化,有些 key 被搬走了,有些 key 則原地不動。這樣,遍歷 map 的結果就不可能按原來的順序了。
最后,也是最有意思的一點。
那如果說我已經初始化好了一個 map,并且不對這個 map 做任何操作,也就是不會發(fā)生擴容,那遍歷順序是固定的嗎?
答:也不是。
Go 杜絕了這種做法,主要是擔心程序員會在開發(fā)過程中依賴穩(wěn)定的遍歷順序,因為這是不對的。
所以在遍歷 map 時,并不是固定地從 0 號 bucket 開始遍歷,每次都是從一個隨機值序號的 bucket 開始遍歷,并且是從這個 bucket 的一個隨機序號的 cell 開始遍歷。
如何順序讀取
如果希望按照特定順序遍歷 map,可以先將鍵或值存儲到切片中,然后對切片進行排序,最后再遍歷切片。
改造一下上面的代碼,讓它按順序輸出:
package main import ( ? ? "fmt" ? ? "sort" ) func main() { ? ? m := map[string]int{ ? ? ? ? "apple": ?1, ? ? ? ? "banana": 2, ? ? ? ? "orange": 3, ? ? } ? ? // 將 map 中的鍵存儲到切片中 ? ? keys := make([]string, 0, len(m)) ? ? for k := range m { ? ? ? ? keys = append(keys, k) ? ? } ? ? // 對切片進行排序 ? ? sort.Strings(keys) ? ? // 按照排序后的順序遍歷 map ? ? for _, k := range keys { ? ? ? ? fmt.Printf("key=%s, value=%d\n", k, m[k]) ? ? } }
在上面的代碼中,首先將 map 中的鍵存儲到一個切片中,然后對切片進行排序。
最后,按照排序后的順序遍歷 map。這樣就可以按照特定順序輸出鍵值對了。
參考文章:
https://golang.design/go-questions/map/unordered/
到此這篇關于Go語言map實現順序讀取的文章就介紹到這了,更多相關Go map順序讀取內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Golang測試func?TestXX(t?*testing.T)的使用詳解
一般Golang中的測試代碼都以xxx_test.go的樣式,在命名測試函數的時候以Testxx開頭,下面給大家介紹Golang測試func?TestXX(t?*testing.T)的使用,感興趣的朋友跟隨小編一起看看吧2024-08-08golang使用iconv報undefined:XXX的問題處理方案
這篇文章主要介紹了golang使用iconv報undefined:XXX的問題處理方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03