HashSet中元素的顺序如何工作?

时间:2014-01-21 15:27:15

标签: java hashset

我知道HashSet中元素的顺序应该是任意的。但出于好奇,有人能告诉我订单是如何确定的吗?

我注意到当我插入两个元素(比如A和B)时,顺序会出现A, B,然后再次重新执行相同的代码会给我B, A,然后重新执行它第三次给我A, B

我的意思是,这有点不确定,有点奇怪。

4 个答案:

答案 0 :(得分:4)

顺序由Hash Map / Set中使用的Hashing算法,该Map的确切设置和对象的Hashcodes确定。

如果您的对象在多次运行中具有一致的哈希码(例如字符串)并且以相同的顺序放置到具有相同设置的地图中,那么通常它们每次都会以相同的顺序出现。如果他们不这样做,他们就不会。

可以在此处看到HashMap的来源:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java

事实上,该来源的有趣引用是:

  

此课程不保证地图的顺序;特别是,它不保证订单会随着时间的推移保持不变。

因此,每次程序运行时,订单不仅可能不同,而且实际上API本身并不能保证即使在程序的一次运行中订单也会保持不变!

“不确定且有点奇怪”是对HashMap的排序的良好描述 - 实际上几乎是文档所说的。如果您想要订购,请使用LinkedHashMapTreeMap。如果您不想订购,那么不要担心,通过使排序有效地随机HashMap给予您极其快速的响应来自它确保行为的方法!

答案 1 :(得分:2)

原则上有两个因素:

  1. 密钥的哈希代码可能是不确定的,当您使用默认的hashCode实现时会出现这种情况,这种实现依赖于内存位置

  2. HashSet本身可以是非确定性的,看看HashMap.initHashSeedAsNeeded(HashSet在标准Oracle SDK中使用HashMap作为基础数据结构),具体取决于它可以使用sun.misc.Hashing.randomHashSeed(this)来初始化{hashSeed {1}}字段,用于计算密钥的

  3. 的hashCode时使用

    随机化对于实现概率性能保证非常重要。 这就是javadoc对hashSeed所说的内容:

      

    / **     *与此实例关联的随机值,应用于
        *使哈希冲突难以找到的密钥哈希码。如果为0则为
        *禁用替代哈希。    * /

答案 2 :(得分:1)

除非您向HashSet添加/删除内容,否则订单不会更改(在实践中)。

订单基于内部hashtable存储桶。这取决于对象的hashCode()和哈希表的大小。

简化示例:

A的哈希码是10,B的hashCode是11. hastable的大小为2。 从哈希码到哈希表中的位置的映射纯粹基于最后一位,即使哈希码进入表[0],奇数进入表[1]。

table[0] = { A }
table[1] = { B }

迭代这些值很可能现在是A,B。只要表格大小保持不变,每次结果都应该是可重复的。

使用hashCode 12添加第三个元素C(当不调整表格大小时)也将它添加到存储桶#0。

table[0] = { A, C }
table[1] = { B }

所以你的迭代将是A,C,B。取决于你是否在C:C,A,B之前插入A

添加元素实际上会调整表的大小并使用调整后的映射重新哈希。例如。表大小将加倍,最后2位可用于确定存储桶

table[0] = { C }
table[1] = {   }
table[2] = { A }
table[3] = { B }

只需添加1个元素就可以完全改变顺序。

答案 3 :(得分:0)

只有HashSet保持和garatuees没有顺序,甚至没有任意顺序(Why can hashCode() return the same value for different objects in Java?)!不要强迫订单! 序列化和反序列化它们,原始订单将被销毁。

使用LinkedHashSet代替HashSet。

相关问题