【GoLang筆記】遍曆map時的key隨機化問題及解決方案

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

之前的一篇筆記曾分析過,Go的map在底層是用hashmap實現的。由於高效的hash函數肯定不是對key做順序散列的,所以,與其它語言實現的hashmap類似,在使用Go語言map過程中,key-value的插入順序與遍曆map時key的訪問順序是不相同的。熟悉hashmap的同學對這個情況應該非常清楚。
所以,本文要提到的肯定不是這個,而是一個比較讓人驚奇的情況,下面開始說明。

1. 通過range遍曆map時,key的順序被隨機化
在golang 1.4版本中,藉助關鍵字range對Go語言的map做遍曆訪問時,前後兩輪遍曆訪問到的key的順序居然是被隨機化的
這個現象在其它語言中是很少見的,比如C語言實現hashmap時,通常會用數組(即一段連續的記憶體空間)來存key,雖然key的分布順序與插入順序不一致,但k-v資料填充完畢後,整個hashmap的key的次序是固定的,所以,後續遍曆這個hashmap時,每輪遍曆訪問到的key的順序是一致的。
但Go語言通過range遍曆map時,確實會對map的key順序做隨機化。下面是一段簡單的驗證程式。

// map_range_rand.gopackage mainimport (    "fmt")func main() {    m := make(map[string]string)    m["hello"] = "echo hello"    m["world"] = "echo world"    m["go"] = "echo go"    m["is"] = "echo is"    m["cool"] = "echo cool"    for k, v := range m {        fmt.Printf("k=%v, v=%v\n", k, v)    }}
在go v1.4環境中,執行go build map_range_rand.go完成編譯後,運行產出的2進位檔案,結果如下。

第1次運行輸出:
$ ./map_range_rand k=is, v=echo isk=cool, v=echo coolk=hello, v=echo hellok=world, v=echo worldk=go, v=echo go
第2次運行輸出:
$ ./map_range_rand k=go, v=echo gok=is, v=echo isk=cool, v=echo coolk=hello, v=echo hellok=world, v=echo world
第3次運行輸出:
$ ./map_range_rand k=hello, v=echo hellok=world, v=echo worldk=go, v=echo gok=is, v=echo isk=cool, v=echo cool
可以很清楚地看到,每次遍曆時,key的順序都是不同的。
後來在golang官方blog的文章Go maps in action中,確認了這個現象確實存在,而且是Go語言的設計者們有意為之,在這篇文章關於Iteration order的說明中提到:
When iterating over a map with a range loop, the iteration order is not specified and is not guaranteed to be the same from one iteration to the next. Since Go 1 the runtime randomizes map iteration order, as programmers relied on the stable iteration order of the previous implementation.
看起來是因為大家在使用Go的map時,可能會在商務邏輯中依賴map key的穩定遍曆順序,而Go底層實現並不保證這一點。因此,Go語言索性對key次序做隨機化,以提醒大家不要依賴range遍曆返回的key次序。
奇怪的是,我在golang 1.2環境中編譯上面的範例程式碼後反覆運行,輸出結果中key的次序是非隨機化的。
不過,不管如何,這個預設的次序肯定是不能依賴的。

2. 業務依賴key次序時,如何解決隨機化問題

其實Go maps in action一文已經給出瞭解決方法:
If you require a stable iteration order you must maintain a separate data structure that specifies that order.
可見,需要另外維護一個資料結構來保持有序的key,然後根據有序key來遍曆map。
下面是本文對上個例子給出的穩定遍曆次序的解決方案。
package mainimport ("fmt"    "sort")func main() {    m := make(map[string]string)    m["hello"] = "echo hello"    m["world"] = "echo world"    m["go"] = "echo go"    m["is"] = "echo is"    m["cool"] = "echo cool"    sorted_keys := make([]string, 0)    for k, _ := range m {        sorted_keys = append(sorted_keys, k)    }      // sort 'string' key in increasing order    sort.Strings(sorted_keys)    for _, k := range sorted_keys {        fmt.Printf("k=%v, v=%v\n", k, m[k])    }}
上面的樣本中,通過引入sort對key做排序,然後根據有序的keys遍曆map即可保證每次遍曆map時的key順序是固定的。
$ go build map_range_rand.go 
可以驗證,每次的執行結果中key的次序都是按字典序進行升序排列的:
$ ./map_range_randk=cool, v=echo coolk=go, v=echo gok=hello, v=echo hellok=is, v=echo isk=world, v=echo world

【參考資料】
Go Blog - Go maps in action

=========================== EOF ==========================







相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.