簡體   English   中英

HashSet與JDK 7/8的順序和區別

[英]HashSet order and difference with JDK 7 / 8

這是一個兩部分問題:

  1. HashSet是否實現了一些隱藏的排序機制,或者只是引用文檔: It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. 告訴我,未來有時可能會改變訂單和/或取決於內存使用情況?
  2. 當我在JDK之間切換時,為什么我會得到完全不同的“排序”(我敢說)?

舉個例子:

for (int i = 0; i < 1000; i++) {
        Set<String> stringSet = new HashSet<>();
        stringSet.add("qwe");
        stringSet.add("rtz");
        stringSet.add("123");
        stringSet.add("qwea");
        stringSet.add("12334rasefasd");
        stringSet.add("asdxasd");
        stringSet.add("arfskt6734");
        stringSet.add("123121");
        stringSet.add("");
        stringSet.add("qwr");
        stringSet.add("rtzz");
        stringSet.add("1234");
        stringSet.add("qwes");
        stringSet.add("1234rasefasd");
        stringSet.add("asdxasdq");
        stringSet.add("arfskt6743");
        stringSet.add("123121 ");
        stringSet.add(" ");
        System.out.println(stringSet);
    }

無論我運行多少次,都會產生以下輸出:

JDK 7: [, , 123, qwea, asdxasdq, qwe, qwr, 123121 , arfskt6743, 1234rasefasd, qwes, rtz, rtzz, 1234, 12334rasefasd, asdxasd, arfskt6734, 123121]

JDK 8: [, , qwes, arfskt6743, asdxasdq, 123121, 123121 , arfskt6734, qwr, 123, 1234, qwea, rtzz, rtz, 12334rasefasd, 1234rasefasd, qwe, asdxasd]

顯然,空字符串和僅空白字符串兩次都是引導方式,但其余部分完全不同。

根據集合更改頁面上的更新

7u6中添加的備用String散列函數已從JDK 8中刪除,同時還有jdk.map.althashing.threshold系統屬性。 相反,包含大量沖突鍵的哈希箱通過將其條目存儲在平衡樹而不是鏈表中來提高性能。 此JDK 8更改僅適用於HashMap,LinkedHashMap和ConcurrentHashMap。

在極少數情況下,此更改可能會引入對HashMap和HashSet的迭代順序的更改。 沒有為HashMap對象指定特定的迭代順序 - 任何依賴於迭代順序的代碼都應該被修復。


所以,基本上

用於散列集合的算法已更改以提高性能 它變為平衡樹而不是鏈表。

這種更改可能會改變您的集合的迭代順序 ,並且已確定您應該修復此類行為 ,如果您依賴它。

你看到了一個更好的集合實現,它可能看起來像是有序的,但它純屬巧合。

我建議你不要依賴集的迭代順序,因為順序不是保證。

@編輯

如用戶Holger所述,另一個概念也很重要,

默認情況下不使用Java 7的“替代字符串散列函數”。 此外,平衡樹僅適用於桶沖突場景。 盡管如此,由於這種改進,還有另一個未提及的變化。 對象的哈希碼到數組位置的映射經歷了從Java 7簡化到Java 8的轉換

HashSet的內部存儲由算法定義。 它不是隨機的。

除了基於散列之外,規范(API)沒有指定任何特定算法。 實現可以選擇它想要的任何算法,並且可以在將來的版本中自由選擇不同的算法。

但是,基於算法,​​這意味着對於任何特定版本的實現(Oracle vs IBM,7 vs 8,...),添加一組特定的值將始終產生相同的結果,即排序。

排序是針對特定版本一致的 ,但它是不確定的,並可能在未來的版本和/或不同的實現更改,恕不另行通知,所以你應該依賴於順序。

要獲得更令人興奮的內容,請將示例代碼更改為

public static void main(String... args) {
  System.out.println(System.getProperty("java.version"));
  List<String> strings=Arrays.asList("qwe", "rtz", "123", "qwea",
      "12334rasefasd", "asdxasd", "arfskt6734", "123121", "", "qwr",
      "rtzz", "1234", "qwes", "1234rasefasd", "asdxasdq", "arfskt6743",
      "123121 ", " ");

  for (int i = 5; i < 26; i++) {
      Set<String> stringSet = new HashSet<>(1<<i);
      stringSet.addAll(strings);
      System.out.println(stringSet);
  }
}

這仍然以相同的順序將相同的字符串添加到HashSet ,但是HashSet已經初始化為具有不同的容量。
結果是

1.7.0_51
[,  , qwea, 123, asdxasdq, qwe, qwr, 123121 , arfskt6743, 1234rasefasd, qwes, rtzz, rtz, 1234, 12334rasefasd, arfskt6734, asdxasd, 123121]
[, qwr, arfskt6743, rtzz, 12334rasefasd,  , qwea, 123, asdxasdq, qwe, 123121 , 1234rasefasd, qwes, rtz, 1234, arfskt6734, asdxasd, 123121]
[, qwr, arfskt6743, rtzz, 12334rasefasd,  , 123, rtz, 1234, arfskt6734, asdxasd, 123121, qwea, asdxasdq, qwe, 123121 , 1234rasefasd, qwes]
[, qwr, arfskt6743, rtzz, 12334rasefasd,  , arfskt6734, asdxasd, 123121, 123121 , 1234rasefasd, 123, rtz, 1234, qwea, asdxasdq, qwe, qwes]
[, rtzz,  , 123121, 123121 , 1234rasefasd, 123, rtz, qwea, asdxasdq, qwe, qwes, qwr, arfskt6743, 12334rasefasd, arfskt6734, asdxasd, 1234]
[, rtzz,  , 123121, 123, asdxasdq, 123121 , 1234rasefasd, rtz, qwea, qwe, qwes, qwr, arfskt6743, 12334rasefasd, arfskt6734, asdxasd, 1234]
[,  , 123121, asdxasdq, 1234rasefasd, rtz, qwea, qwes, arfskt6743, 12334rasefasd, arfskt6734, asdxasd, rtzz, 123, 123121 , qwe, qwr, 1234]
[,  , 123121, 1234rasefasd, rtz, qwea, qwes, asdxasd, rtzz, 123, 1234, asdxasdq, arfskt6743, 12334rasefasd, arfskt6734, 123121 , qwe, qwr]
[,  , rtz, asdxasd, rtzz, arfskt6743, 12334rasefasd, arfskt6734, 123121 , qwe, qwr, 123121, 1234rasefasd, qwea, qwes, 123, 1234, asdxasdq]
[,  , arfskt6743, 12334rasefasd, arfskt6734, qwea, qwes, 1234, asdxasdq, rtz, asdxasd, rtzz, 123121 , qwe, qwr, 123121, 1234rasefasd, 123]
[,  , qwea, qwes, rtz, asdxasd, rtzz, 123121 , qwe, qwr, 123, arfskt6743, 12334rasefasd, arfskt6734, 1234, asdxasdq, 123121, 1234rasefasd]
[,  , qwea, qwes, asdxasd, 1234, asdxasdq, 123121, 1234rasefasd, rtz, rtzz, 123121 , qwe, qwr, 123, arfskt6743, 12334rasefasd, arfskt6734]
[,  , qwea, qwes, asdxasd, 1234, 1234rasefasd, rtzz, 123121 , 123, arfskt6743, arfskt6734, asdxasdq, 123121, rtz, qwe, qwr, 12334rasefasd]
[,  , 1234rasefasd, 123, asdxasdq, rtz, qwe, qwr, 12334rasefasd, qwea, qwes, asdxasd, 1234, rtzz, 123121 , arfskt6743, arfskt6734, 123121]
[,  , 123, asdxasdq, rtz, qwe, qwr, 12334rasefasd, 123121 , 123121, 1234rasefasd, qwea, qwes, asdxasd, 1234, rtzz, arfskt6743, arfskt6734]
[,  , 123, asdxasdq, rtz, qwe, qwr, 12334rasefasd, 1234rasefasd, qwea, qwes, asdxasd, 1234, rtzz, 123121 , 123121, arfskt6743, arfskt6734]
[,  , 123, rtz, qwe, qwr, 12334rasefasd, asdxasd, asdxasdq, 1234rasefasd, qwea, qwes, 1234, rtzz, 123121 , 123121, arfskt6743, arfskt6734]
[,  , 123, rtz, qwe, qwr, asdxasd, asdxasdq, 1234, arfskt6743, arfskt6734, 12334rasefasd, 1234rasefasd, qwea, qwes, rtzz, 123121 , 123121]
[,  , 123, rtz, qwe, qwr, asdxasdq, 1234, 12334rasefasd, 1234rasefasd, qwea, qwes, rtzz, 123121 , 123121, asdxasd, arfskt6743, arfskt6734]
[,  , 123, rtz, qwe, qwr, 1234, 12334rasefasd, qwea, qwes, rtzz, 123121 , asdxasdq, 1234rasefasd, 123121, asdxasd, arfskt6743, arfskt6734]
[,  , 123, rtz, qwe, qwr, 1234, qwea, qwes, rtzz, 12334rasefasd, 123121 , asdxasdq, 1234rasefasd, 123121, asdxasd, arfskt6743, arfskt6734]
1.8.0_111
[,  , qwes, arfskt6743, asdxasdq, 123121, 123121 , arfskt6734, qwr, 123, 1234, qwea, rtzz, rtz, 12334rasefasd, 1234rasefasd, qwe, asdxasd]
[, 123121, arfskt6734, qwr, 1234, asdxasd,  , qwes, arfskt6743, asdxasdq, 123121 , 123, qwea, rtzz, rtz, 12334rasefasd, 1234rasefasd, qwe]
[, arfskt6734, qwr,  , arfskt6743, asdxasdq, 123, rtzz, 123121, 1234, asdxasd, qwes, 123121 , qwea, rtz, 12334rasefasd, 1234rasefasd, qwe]
[, qwr,  , 123, rtzz, 1234, asdxasd, qwes, 123121 , qwea, rtz, 1234rasefasd, arfskt6734, arfskt6743, asdxasdq, 123121, 12334rasefasd, qwe]
[,  , 123, 1234, rtz, arfskt6734, arfskt6743, asdxasdq, 123121, 12334rasefasd, qwe, qwr, rtzz, asdxasd, qwes, 123121 , qwea, 1234rasefasd]
[,  , 1234, asdxasdq, 123121, 12334rasefasd, rtzz, asdxasd, qwes, 123121 , qwea, 123, rtz, arfskt6734, arfskt6743, qwe, qwr, 1234rasefasd]
[,  , 1234, 12334rasefasd, qwes, qwea, rtz, asdxasdq, 123121, rtzz, asdxasd, 123121 , 123, arfskt6734, arfskt6743, qwe, qwr, 1234rasefasd]
[,  , asdxasdq, rtzz, 123121 , arfskt6734, arfskt6743, qwe, qwr, 1234, 12334rasefasd, qwes, qwea, rtz, 123121, asdxasd, 123, 1234rasefasd]
[,  , asdxasdq, 123121 , arfskt6734, arfskt6743, 1234, 12334rasefasd, qwes, qwea, 123121, asdxasd, 1234rasefasd, rtzz, qwe, qwr, rtz, 123]
[,  , asdxasdq, 1234, rtzz, 123121 , arfskt6734, arfskt6743, 12334rasefasd, qwes, qwea, 123121, asdxasd, 1234rasefasd, qwe, qwr, rtz, 123]
[,  , 1234, rtzz, 123121 , arfskt6734, arfskt6743, 12334rasefasd, qwes, qwea, 123121, asdxasd, qwe, qwr, rtz, 123, asdxasdq, 1234rasefasd]
[,  , 1234, 123121 , arfskt6734, arfskt6743, qwes, qwea, asdxasd, rtzz, 12334rasefasd, 123121, qwe, qwr, rtz, 123, asdxasdq, 1234rasefasd]
[,  , arfskt6734, arfskt6743, asdxasd, 123, asdxasdq, 1234rasefasd, 1234, 123121 , qwes, qwea, rtzz, 12334rasefasd, 123121, qwe, qwr, rtz]
[,  , arfskt6734, arfskt6743, 123, asdxasdq, 123121 , qwes, qwea, rtzz, 12334rasefasd, 123121, qwe, qwr, rtz, asdxasd, 1234rasefasd, 1234]
[,  , 123, 123121 , qwe, qwr, rtz, asdxasd, 1234rasefasd, arfskt6734, arfskt6743, asdxasdq, qwes, qwea, rtzz, 12334rasefasd, 123121, 1234]
[,  , 123, qwe, qwr, rtz, asdxasd, 1234rasefasd, arfskt6734, arfskt6743, qwes, qwea, rtzz, 123121, 1234, 123121 , asdxasdq, 12334rasefasd]
[,  , 123, qwe, qwr, rtz, arfskt6734, arfskt6743, 123121 , asdxasdq, asdxasd, 1234rasefasd, qwes, qwea, rtzz, 123121, 1234, 12334rasefasd]
[,  , 123, qwe, qwr, rtz, arfskt6734, arfskt6743, 123121 , 1234, 12334rasefasd, asdxasdq, asdxasd, 1234rasefasd, qwes, qwea, rtzz, 123121]
[,  , 123, qwe, qwr, rtz, arfskt6734, arfskt6743, 1234, asdxasdq, asdxasd, 1234rasefasd, qwes, qwea, rtzz, 123121 , 12334rasefasd, 123121]
[,  , 123, qwe, qwr, rtz, 1234, asdxasdq, asdxasd, qwes, qwea, rtzz, 123121 , 12334rasefasd, 123121, arfskt6734, arfskt6743, 1234rasefasd]
[,  , 123, qwe, qwr, rtz, 1234, qwes, qwea, rtzz, 123121 , 123121, arfskt6734, arfskt6743, asdxasdq, asdxasd, 12334rasefasd, 1234rasefasd]

這表明迭代順序不僅取決於特定的實現,還取決於HashSet的歷史。 更高的容量也可能是因為之前更大但刪除了元素。

雖然哈希碼確定要用於元素的陣列位置,但也可能存在沖突,導致元素共享條目。 在該條目中,沖突可能通過鏈表解析,在這種情況下,此存儲桶中的順序反映了插入順序,因此它還取決於集合的歷史記錄,或者可能通過使用自Java 8以來的平衡樹來解決,將反映其中一個,即哈希碼或元素的自然順序的順序,具體取決於它是真正的哈希沖突還是只是桶沖突

但是,如果存儲桶中存在一定數量的沖突,則Java 8的HashSet將僅使用樹,否則,它還使用鏈表。 為了避免在這些變體之間來回切換,它使用不同的閾值來轉換為樹並轉換回鏈接列表。 因此,如果碰撞的數量介於這些閾值之間,它將再次取決於集合的歷史,即先前是否存在更多元素,哪些形式以及因此具有哪個順序。


請注意,默認情況下禁用Java 7的“替代字符串散列函數”,並且沖突解決方案解決了一個極端情況。 但是,正如您從輸出中看到的那樣,迭代順序幾乎總是存在顯着差異。

原因在於,現在可以更有效地處理沖突,減少了避免沖突的嘗試。 Java 7中 ,哈希碼在映射到數組位置之前經歷了以下轉換:

 h ^= (h >>> 20) ^ (h >>> 12);
 return h ^ (h >>> 7) ^ (h >>> 4);

相比之下, Java 8使用以下轉換:

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

即使沒有發生沖突,這也會對迭代順序產生直接影響。

答案1:是的Hashset不維護插入順序,但在此之后如果你迭代它,你每次都會得到相同的順序。

回答2:迭代結果可能與java版本不同,因為它依賴於該版本的哈希碼實現。 但是Hashset提供了一個保證,迭代順序永遠不會得到更改意味着在插入元素之后,如果每次在java版本中獲得相同的順序時迭代它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM