似乎Java的HashMap 总是的实现将键放在相同的bin中(至少我用Integer键看到了)。即散列是确定性的,并且在所有运行中它产生相同的值
我听说有些语言将插入随机化,以便出于安全原因,密钥存储在哪个存储桶中是不可预测的。
为什么Java的键总是一样的?
答案 0 :(得分:5)
Java 7不是这样,它为每个HashMap实例添加了一个唯一的哈希种子。有关Collections Framework Enhancements in Java SE 7页面的更多信息。
为了提高性能,已在Java 8中删除了此机制,并将其替换为将可比较的密钥(例如String)转换为平衡树以替代DoS安全问题的替代方法。有关Collections Framework Enhancements in Java SE 8页面的更多信息。
答案 1 :(得分:4)
此处感兴趣的攻击是拒绝服务(DoS)。攻击者选择一组击中同一个桶的键。这将地图操作的性能从O(1)转换为O(n)。这样做n次(构建地图说),我们从O(n)到O(n ^ 2)。还有定时攻击的可能性,但我会方便地忽略它。
通常,大多数库代码都假定不需要采取任何措施来避免DoS。但是,最近一些Java实现使用MURMUR哈希来随机化String
的哈希函数以避免某些攻击。 MURMUR将每进程随机数混合到哈希码的生成中,使得该功能对于该进程是稳定的,但是从外部难以理解(但不一定是不可能的)。最近,如果存在过多的冲突并且密钥正确地实现了Comparable
,那么这已被替换为回退到树结构。
如果您在发现代码的情况下担心此类攻击,可以使用其他Map
实施,例如java.util.TreeMap
。
答案 2 :(得分:1)
在Java中,单个类负责实现默认的hashCode()方法(或继承Object
类中的一个),并且复杂的安全方案(如击败高级DoS攻击)不是{{{{ {1}},Object
等
因此,大多数类使用非常简单,快速的实现,试图在常见情况下确保均匀分布。
如果您认为实施自定义哈希策略很重要,无论是因为您想避免黑客攻击,还是因为您知道您的特定用法可能会导致与默认方法发生大量冲突,可以使用Gnu Trove's Integer
之类的集合,它允许您提供特定于集合实例的自定义散列策略。
答案 3 :(得分:0)
要完成其他答案,我会提到当HashMap
的密钥使用内置Object.hashCode
时,i。即不要重写此方法,这是一种常见的情况,Object的hashCode是使用随机数生成器计算的,这使得整个系统的行为不那么具有确定性。有关详细信息,请参阅此问题:Java Object.hashCode() - address or random()?。