簡體   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