我有一个导航栏作为地图:
var navbar = map[string]navbarTab{
}
navbarTab
具有各种属性,子项等。当我尝试渲染导航栏(使用for tabKey := range navbar
)时,它会以随机顺序显示。我知道range
在运行时会随机排序,但似乎无法获得有序的键列表或按插入顺序迭代。
游乐场链接在这里:http://play.golang.org/p/nSL1zhadg5虽然它似乎没有表现出相同的行为。
如何在不破坏广告订单的情况下迭代此地图?
答案 0 :(得分:24)
地图数据结构的通用概念是它是键值对的集合。 “有序”或“已排序”未提及。
在计算机科学中,关联数组,地图,符号表或字典是抽象数据类型由
(key, value)
对的集合组成,这样每个可能的键只在集合中出现一次。
map 是计算机科学中最有用的数据结构之一,因此Go将其作为内置类型提供。但是,语言规范仅指定常规映射(Map types):
映射是一种无序的一组元素,称为元素类型,由一组另一种类型的唯一键索引,称为键类型。未初始化地图的值为
nil
。
请注意,语言规范不仅省略了“有序”或“已排序”字词,而是明确指出了相反的意思:“unordered”。但为什么?因为这为运行时提供了更大的自由度来实现地图类型。语言规范允许使用任何地图实现,如 hash map, tree map等。请注意,Go的当前(和以前的)版本使用哈希映射实现,但是你不需要知道使用它。
关于这个问题,必须阅读博客文章Go maps in action。
在Go 1之前,当未更改地图时,当您多次迭代其键/条目时,运行时以相同的顺序返回键。请注意,如果修改了地图,则此订单可能会更改,因为实施可能需要执行重新整理以容纳更多条目。人们开始依赖相同的迭代顺序(当地图未被更改时),因此从Go 1开始,运行时随机数映射迭代顺序故意,以引起开发人员注意订单未定义并且不能依赖。
该怎么做?
如果您需要已排序数据集(无论是键值对的集合还是其他任何内容),无论是按键类型或任意顺序定义的插入顺序或自然顺序,{{1不是正确的选择。如果您需要预定义的订单,切片(和数组)就是您的朋友。如果您需要能够通过预定义的键查找元素,则可以另外从切片构建一个映射,以允许通过键快速查找元素
首先构建map
然后以正确的顺序构建切片,或者首先构建切片,然后从中构建map
完全取决于您。
前面提到的 Go map in action 博客文章中有一节专门介绍Iteration order:
当使用范围循环迭代映射时,未指定迭代顺序,并且不保证从一次迭代到下一次迭代是相同的。由于Go 1,运行时随机化地图迭代顺序,因为程序员依赖于先前实现的稳定迭代顺序。如果需要稳定的迭代顺序,则必须维护一个指定该顺序的单独数据结构。此示例使用单独的排序键片段按键顺序打印
map
:
map[int]string
<强> P.S:强>
......虽然它似乎没有表现出相同的行为。
您似乎在Go Playground上看到“相同的迭代顺序”,因为Go Playground上的应用程序/代码的输出是缓存。一旦执行了新的但唯一的代码,其输出将保存为新的。执行相同的代码后,将显示保存的输出,而无需再次运行代码。所以基本上它与您看到的迭代顺序不同,它是完全相同的输出而不再执行任何代码。
答案 1 :(得分:18)
Go地图不保留插入顺序;你必须自己实现这种行为。
示例:
type NavigationMap struct {
m map[string]navbarTab
keys []string
}
func NewNavigationMap() *NavigationMap { ... }
func (n *NavigationMap) Set(k string, v navbarTab) {
n.m[k] = v
n.keys = append(n.keys, k)
}
此示例不完整,并未涵盖所有用例(例如,更新重复键上的插入顺序)。
如果您的用例包括多次重新插入相同的密钥(如果密钥 k 已经在地图中,则不会更新 k 的插入顺序):
func (n *NavigationMap) Set(k string, v navbarTab) {
_, present := n.m[k]
n.m[k] = v
if !present {
n.keys = append(n.keys, k)
}
}
选择满足您要求的最简单的东西。