【发布时间】:2019-04-30 17:13:24
【问题描述】:
从 Golang source code 来看,它们似乎遵循哈希表的非常标准的实现(即桶数组)。基于此,对于未更改的映射,迭代似乎应该是确定性的(即按顺序迭代数组,然后按顺序在桶内迭代)。为什么他们让迭代随机?
【问题讨论】:
标签: dictionary go
从 Golang source code 来看,它们似乎遵循哈希表的非常标准的实现(即桶数组)。基于此,对于未更改的映射,迭代似乎应该是确定性的(即按顺序迭代数组,然后按顺序在桶内迭代)。为什么他们让迭代随机?
【问题讨论】:
标签: dictionary go
TL;DR;他们故意从 Go 1 开始使其随机化,以使开发人员不依赖它(不依赖于特定的迭代顺序,该顺序可能会从发布到发布,从平台到平台,甚至可能当地图内部因容纳更多元素而发生变化时,在应用程序的单个运行时期间发生变化。
The Go Blog: Go maps in action: Iteration order:
在使用范围循环对地图进行迭代时,未指定迭代顺序,也不保证从一次迭代到下一次迭代顺序相同。自 Go 1.0 发布以来,运行时已随机映射迭代顺序。 程序员已经开始依赖 Go 早期版本的稳定迭代顺序,这在不同的实现之间存在差异,从而导致可移植性错误。如果您需要稳定的迭代顺序,您必须维护一个单独的数据结构,该结构指定顺序。
还有Go 1 Release Notes: Iterating in maps:
旧的语言规范没有定义地图的迭代顺序,实际上它在硬件平台上有所不同。 这导致迭代地图的测试脆弱且不可移植,具有令人不快的特性,即测试可能总是在一台机器上通过但在另一台机器上中断。
在 Go 1 中,使用 for range 语句在地图上迭代时访问元素的顺序被定义为不可预测,即使同一个循环在同一个地图上运行多次也是如此。代码不应假定元素以任何特定顺序被访问。
这种变化意味着依赖于迭代顺序的代码很可能会提前中断并在它成为问题之前很久就被修复。同样重要的是,它允许地图实现确保更好的地图平衡,即使程序使用范围循环从地图中选择元素。
请注意,使用for range 在地图上测距时适用“随机”顺序。
对于可重现的输出(为了便于测试和它带来的其他便利),标准库在许多地方对映射键进行排序:
encoding/json
json 包使用排序键编组映射。引用json.Marshal():
地图值编码为 JSON 对象。映射的键类型必须是字符串、整数类型或实现 encoding.TextMarshaler。 地图键被排序并通过应用以下规则用作 JSON 对象键,但要遵守上述字符串值的 UTF-8 强制:
- 直接使用任何字符串类型的键
- encoding.TextMarshalers 被封送
- 整数键转换为字符串
fmt包从Go 1.12 开始,fmt 包使用排序键打印地图。引用发行说明:
地图现在以按键排序的顺序打印以简化测试。排序规则是:
- 适用时,nil 比较低
- 整数、浮点数和字符串按 排序
- NaN 比较小于非 NaN 浮点数
- bool 在真之前比较假
- 复数比较实数,然后是虚数
- 指针按机器地址比较
- 按机器地址比较通道值
- 结构体依次比较每个字段
- 数组依次比较每个元素
- 接口值首先通过 reflect.Type 描述具体的 > - 类型进行比较,然后根据前面规则中描述的具体值进行比较。
text/template 和 html/template 包的 {{range}} 操作也按排序键顺序访问元素。引用text/template的包文档:
{{range pipeline}} T1 {{end}} The value of the pipeline must be an array, slice, map, or channel. If the value of the pipeline has length zero, nothing is output; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed. If the value is a map and the keys are of basic type with a defined order, the elements will be visited in sorted key order.
【讨论】:
1、2、3,您可以编写一个预期此顺序的测试,这个测试可能总是成功的。然后在下一个 Go 版本中,它可能在没有明显原因的情况下失败(如果新的或更改的地图实现将以不同的顺序迭代)...
go 将初始“盐”添加到哈希中 - 因此不会导致性能损失 - 但会在针对同一数据集的多次运行期间产生随机性错觉。