我有一个导航栏作为Map:
var navbar = map[string]navbarTab{
}
其中navbarTab
有各种属性,子项等等。当我尝试渲染导航栏(使用for tabKey := range navbar
)时,它以随机顺序显示。我知道range
在运行时会随机排序,但似乎没有办法获得有序的键列表或以插入顺序迭代。
Playground链接在这里:http://play.golang.org/p/nSL1zhadg5,尽管它似乎没有表现出相同的行为。
如何在不破坏插入顺序的情况下迭代这个Map?
2条答案
按热度按时间eh57zj3b1#
map数据结构的 * 一般 * 概念是它是一个键值对的集合。"有序" 或 "排序" 没有提到。
Wikipedia definition:
在计算机科学中,关联数组、Map、符号表或字典是一种抽象数据类型,由
(key, value)
对的集合组成,这样每个可能的键在集合中只出现一次。一个map是一组无序的元素,它们的类型称为元素类型,由另一种类型的唯一键索引,称为键类型。未初始化的map的值是
nil
。请注意,语言规范不仅省略了 "ordered" 或 "sorted" 这些词,它还明确指出了相反的情况:* "unordered"**。但是为什么呢?因为这给了运行时 * 更大的自由度 * 来实现map类型。语言规范允许使用任何map实现,比如 * hash * map, tree * map等。注意,当前(和以前)版本的Go使用hash map实现,但是你不需要知道它就可以使用它。
博客文章Go maps in action是关于这个问题的必读文章。
在Go 1之前,当map没有改变时,当你多次迭代它的键/条目时,运行时会以相同的顺序返回键。注意,如果map被修改,这个顺序可能会改变,因为实现可能需要重新散列以容纳更多的条目。人们开始依赖相同的迭代顺序(当map没有改变时),所以从Go 1开始,运行时随机化map迭代顺序故意以引起开发人员的注意,顺序没有定义并且不能依赖。
那怎么办?
如果你需要一个 * sorted * 数据集(无论是键值对的集合还是其他任何东西),无论是按插入顺序,还是按键类型定义的自然顺序,还是按任意顺序,
map
都不是正确的选择。(和数组)是你的朋友。如果你需要能够通过预定义的键查找元素,你可以 * 另外 * 从切片中构建一个Map,以允许通过 * 键 * 快速查找元素。您可以先构建
map
,然后按正确的顺序构建一个切片,或者先构建切片,然后再构建map
,这完全取决于您。前面提到的 * Go maps in action * 博客中有一节专门讨论迭代顺序:
当使用range循环迭代map时,迭代顺序没有指定,并且不能保证从一个迭代到下一个迭代都是相同的。由于Go 1运行时随机化map迭代顺序,因为程序员依赖于前一个实现的稳定的迭代顺序。如果你需要一个稳定的迭代顺序,你必须维护一个单独的数据结构来指定那个顺序。这个例子使用一个单独的排序的key切片来按key顺序打印一个
map[int]string
:虽然它似乎没有表现出同样的行为。
看起来你在Go Playground上看到的是“相同的迭代顺序”,因为Go Playground上的应用程序/代码的输出都被缓存了。一旦执行了一个新的、唯一的代码,它的输出就会被保存为新的。一旦执行了相同的代码,保存的输出就会显示出来,而不会再次运行代码。所以基本上它和你看到的迭代顺序不一样,这是完全相同的输出,而无需再次执行任何代码。
虽然使用
for range
的迭代顺序是“随机的”,但在标准库中有一些明显的例外,即encoding/json
,text/template
,html/template
和fmt
包。有关更多详细信息,请参见In Golang,why are iterations over maps random?jyztefdp2#
GoMap不维护插入顺序;您必须自己实现此行为。
示例:
此示例并不完整,并且没有涵盖所有用例(例如,更新重复键的插入顺序)。
如果您的用例包括多次重新插入相同的键(如果键 k 已经在Map中,则这将不会更新键 k 的插入顺序):
选择最简单的东西来满足你的需求。