Golang Map key无序
头秃猫轻王 人气:0一、为什么是无序的?
开门见山,先上源码
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
加载全部内容