我知道HashSet中元素的顺序应该是任意的。但出于好奇,有人能告诉我订单是如何确定的吗?
我注意到当我插入两个元素(比如A和B)时,顺序会出现A, B
,然后再次重新执行相同的代码会给我B, A
,然后重新执行它第三次给我A, B
。
我的意思是,这有点不确定,有点奇怪。
答案 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
的排序的良好描述 - 实际上几乎是文档所说的。如果您想要订购,请使用LinkedHashMap
或TreeMap
。如果您不想订购,那么不要担心,通过使排序有效地随机HashMap
给予您极其快速的响应来自它确保行为的方法!
答案 1 :(得分:2)
原则上有两个因素:
密钥的哈希代码可能是不确定的,当您使用默认的hashCode实现时会出现这种情况,这种实现依赖于内存位置
HashSet本身可以是非确定性的,看看HashMap.initHashSeedAsNeeded
(HashSet在标准Oracle SDK中使用HashMap作为基础数据结构),具体取决于它可以使用sun.misc.Hashing.randomHashSeed(this)
来初始化{hashSeed
{1}}字段,用于计算密钥的
随机化对于实现概率性能保证非常重要。 这就是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。