让我们看看以下Go代码:
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, 74.39967,
}
m["test"] = Vertex{
12.0, 100,
}
fmt.Println(m["Bell Labs"])
fmt.Println(m)
}
输出:
{40.68433 74.39967}
map[Bell Labs:{40.68433 74.39967} test:{12 100}]
但是,如果我更改一个测试顶点声明的次要部分,则移动右侧“}
”4个空格,如下所示:
m["test"] = Vertex{
12.0, 100,
}
..然后输出更改为:
{40.68433 74.39967}
map[test:{12 100} Bell Labs:{40.68433 74.39967}]
为什么这个小修改会影响我的地图顺序?
答案 0 :(得分:13)
映射“顺序”取决于使用的散列函数。哈希函数是随机的,以防止使用哈希冲突的拒绝服务攻击。有关详细信息,请参阅问题跟踪器:
http://code.google.com/p/go/issues/detail?id=2630
根据规范不保证地图顺序。虽然在当前的go实现中没有完成,但是未来的实现可以在GC或其他操作期间进行一些压缩,这些操作会改变映射的顺序而不会使代码修改映射。假设未在规范中定义的属性是不明智的。
地图是一种类型的无序元素组,称为元素类型,由一组另一种类型的唯一键索引,称为键类型。
答案 1 :(得分:7)
地图不应始终按任何固定顺序打印其关键元素:
请参阅“Go: what determines the iteration order for map keys?”
然而,在最新的Go每周版本中(以及可能预期在本月发布的Go1中),迭代顺序是随机的(它从伪随机选择的密钥开始,并且哈希码计算用伪随机数) 如果使用每周版本(以及使用Go1)编译程序,则每次运行程序时迭代顺序都会不同。
尽管(Ref Map Type):
,但它并没有像规范那样完全拼写出来地图是一种类型的 无序 元素组,称为元素类型,由一组另一种类型的唯一键索引,称为键类型。
实际上,规格确实拼写出来,但在For statement section:
未指定地图上的迭代顺序,并且不保证从一次迭代到下一次迭代相同。
- 如果在迭代期间删除尚未到达的映射条目,则不会生成相应的迭代值。
- 如果在迭代期间插入了映射条目,则行为依赖于实现,但每个条目的迭代值最多只会产生一次。
- 如果地图为零,则迭代次数为0.
这是code review 5285042于2011年10月提出的:
运行时:地图迭代的随机偏移
“阻止人们做坏事”的原因似乎特别薄弱 避免恶意哈希冲突更有意义 此外,指向代码的指针使得在开发中可以在存在难以处理的间歇性错误的情况下恢复该行为。
Patrick Mylund Nielsen回复:
Dan的说明实际上是为什么Python开发人员不愿意采用哈希IV随机化的主要论点 - 它打破了他们的单元测试! PHP最终选择不执行此操作,而是限制
http.Request
标头的大小,Oracle和其他人根本不认为这是一个语言问题。
Perl看到了这个问题并应用了类似于Go的修复程序,该程序包含在2003年的Perl 5.8.1中 我可能错了,但我认为当这篇论文被提出时,他们是唯一真正关心的人:“Denial of Service via Algorithmic Complexity Attacks”,攻击哈希表。
(最坏情况的哈希表冲突)对于其他人来说,这在一年前变得非常受欢迎,这是一个很好的激励因素:
“28c3: Effective Denial of Service attacks against web application platforms (YouTube video, December 2011)”,显示了大多数流行网络实施中的常见缺陷 编程语言和平台(包括PHP,ASP.NET,Java等)可以(ab)用于强制Web应用程序服务器在单个HTTP请求中使用99%的CPU几分钟到几个小时。
此攻击主要独立于底层Web应用程序 依赖于Web应用程序服务器通常如何工作的常见事实。