繁体   English   中英

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

[英]How does the order of elements in a HashSet work?

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

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

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

顺序由哈希映射/集合中使用的哈希算法,该映射的精确设置和对象的哈希代码确定。

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

可以在这里看到HashMap的源代码: http//grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java

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

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

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

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

原则上有两个因素:

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

  2. HashSet本身可以是非确定性的,看看HashMap.initHashSeedAsNeeded (HashSet在标准Oracle SDK中使用HashMap作为底层数据结构),根据一些因素,它可以使用sun.misc.Hashing.randomHashSeed(this)来初始化hashSeed字段,这是然后在计算密钥的hashCode时使用

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

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

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

订单基于内部哈希表桶。 这取决于对象的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个元素,订单就会完全改变。

只有HashSet保持和garatuees没有顺序,甚至没有任意顺序( 为什么hashCode()为Java中的不同对象返回相同的值? )! 不要强迫订单! 序列化和反序列化它们,原始订单将被销毁。

使用LinkedHashSet而不是HashSet。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM