首頁 > 軟體

詳解Golang Map中的key為什麼是無序的

2022-02-17 13:01:10

一、為什麼是無序的?

開門見山,先上原始碼

func mapiterinit(t *maptype, h *hmap, it *hiter) {
 
	// decide where to start
	r := uintptr(fastrand())
	if h.B > 31-bucketCntBits {
		r += uintptr(fastrand()) << 31
	}
	
	mapiternext(it)
}

Go 當我們在遍歷 map 時,並不是固定地從第一個數開始遍歷,每次都是從一個位置開始遍歷。即使是一個不會改變的的 map,僅僅只是遍歷它,也不太可能會返回一個固定順序了。
也就是說,GO語言從語言上進行 Map 的無序。

二、GO 為什麼要這麼做?

其實主要是因為 map 在擴容後,可能會將部分 key 移至新記憶體,那麼這一部分實際上就已經是無序的了。而遍歷的過程,其實就是按順序遍歷記憶體地址,同時按順序遍歷記憶體地址中的 key。但這時已經是無序的了。

當然有人會說,如果我就一個 map,我保證不會對 map 進行修改刪除等操作,那麼按理說沒有擴容就不會發生改變。但也是因為這樣,GO 才在原始碼中
加上隨機的元素,將遍歷 map 的順序隨機化,用來防止使用者用來順序遍歷。而這是有風險的程式碼,在GO 的嚴格語法規則下,是堅決不提倡的。

三、遍歷是否真的無序的

1.第一次遍歷

程式碼如下(範例):

package main

import "fmt"

func main() {

	noSortMap := map[int]int{
		1: 1,
		2: 2,
		3: 3,
		4: 4,
		5: 5,
		6: 6,
	}

	for k, v := range noSortMap {

		fmt.Println("key: ", k, "value: ", v)
	}

}

結果果然不出所料,並沒有從第一個數開始

2.第二次遍歷

程式碼同上:

結果果然不出所料,與第一次都不相同

四、如何才能得到有序的鍵值對

我們需要使用 切片(Slice) 來進行控制,

1.詳細程式碼

程式碼如下(範例):

package main

import (
	"fmt"
	"sort"
)

func main() {

	noSortMap := map[int]int{
		1: 1,
		2: 2,
		3: 3,
		4: 4,
		5: 5,
		6: 6,
	}

	var noSortSlice []int
	for k, v := range noSortMap {

		noSortSlice = append(noSortSlice, k)
		fmt.Println("key: ", k, "value: ", v)
	}

	fmt.Println(noSortSlice)
	// 排序
	sort.Ints(noSortSlice)
	sortSlice := noSortSlice
	fmt.Println(sortSlice)
	for _, k := range sortSlice {

		fmt.Println("key: ", k, "value: ", noSortMap[k])
	}
	
}

先將無序的key 放進切片中

再將無序的切片 呼叫 sort包的 Ints 方法排序

排序後再遍歷切片,此時切片有序,則 map 的鍵值對也是有序的

總結

不要依賴map遍歷時返回的key順序,採用隨機選擇遍歷起始位置的方式使得遍歷時返回是亂序的。如果想得到有序鍵值,請依靠有序切片進行存取來得到有效的有序 Map

到此這篇關於詳解Golang Map中的key為什麼是無序的的文章就介紹到這了,更多相關Golang Map key無序內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com