簡體   English   中英

使用Java 8 Streams API,在調用Collectors.toSet()時可以依賴sorted()嗎?

[英]Using the Java 8 Streams API, can sorted() be relied upon when calling Collectors.toSet()?

這是java.util.stream.Collectors類的toSet()方法的實現:

public static <T>
Collector<T, ?, Set<T>> toSet() {
    return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_UNORDERED_ID);
}

我們可以看到,它使用HashSet並調用add HashSet 文檔中 ,“它不能保證集合的迭代順序;特別是,它不能保證訂單在一段時間內保持不變。”

在下面的代碼,一個ListString是流,分類收集到Set

public static void main(String[] args) {
    Set<String> strings = Arrays.asList("c", "a", "b")
            .stream()
            .sorted()
            .collect(Collectors.toSet());
    System.out.println(strings.getClass());
    System.out.println(strings);
}

這提供了輸出:

class java.util.HashSet

[a, b, c]

輸出已排序。 我認為這里發生的事情是,盡管HashSet文檔提供的合同規定了排序不是它提供的,但實現恰好按順序添加。 我想這可能會在未來的版本中發生變化/在JVM之間變化,而更明智的方法是做一些像Collectors.toCollection(TreeSet::new)這樣的事情。

調用Collectors.toSet()時可以依賴sorted() Collectors.toSet()嗎?

此外,“它不能保證訂單在一段時間內保持不變”究竟是什么意思? (我想addremove ,調整底層數組的大小?)

答案是不。 將項目添加到集合后,您不能依賴任何訂單。 來自JDK源代碼(HashSet.java):

/**
 * Returns an iterator over the elements in this set.  The elements
 * are returned in no particular order.
 *
 * @return an Iterator over the elements in this set
 * @see ConcurrentModificationException
 */
public Iterator<E> iterator() {
    return map.keySet().iterator();
}

現在,在JDK的早期版本中,即使訂單無法保證,您通常也會以相同的插入順序獲取項目(除非對象的類實現了hashCode() ,然后您將獲得訂單由hashCode()決定。 要么是對象的創建順序,要么是對象上hashCode()的調用順序。 正如@Holgar在下面的評論中提到的,在HotSpot中它是后者。 你甚至不能指望它,因為這也有例外,因為序列號不是hashCode生成器中的唯一成分。

我最近聽到了Stuart Marks (負責重寫Java 9中Collections主要部分的人)的演講,他說他們已經將隨機化添加到集合的迭代順序(由新的集合工廠創建)在Java 9中。如果你想聽到會話,他談到的部分就會從這里開始 - 好的談話,強烈推薦的方式!

因此,即使您曾經依賴於集合的迭代順序,一旦轉移到Java 9,您應該停止這樣做。

總而言之,如果您需要訂購,您應該考慮使用SortedSetLinkedHashSetTreeSet

要回答這個問題,您必須了解HashSet的實現方式。 顧名思義, HashSet是使用哈希表實現的 基本上,哈希表是由元素哈希索引的數組。 散列函數(在Java中,對象的散列由object.hashCode()計算)基本上是滿足一些條件的函數:

  • 它(相對)快速計算給定元素
  • 兩個對象.equals()彼此具有相同的哈希值
  • 不同項目具有相同散列的概率很小

因此,當你修改一個“已排序”的HashSet (它被理解為“迭代器保留元素的自然順序”)時,這是由於幾個巧合:

  • 元素的自然順序尊重其hashCode的自然順序
  • 哈希表足夠小,不會發生沖突(兩個元素具有相同的哈希碼)

如果查看StringhashCode()方法,您將看到對於單字母字符串,哈希代碼對應於字母的Unicode索引(代碼點) - 因此在這種特定情況下,只要哈希表是足夠小,元素將被排序。 然而,這是一個巨大的巧合

  • 不會保留任何其他排序順序
  • 不適用於hashCodes不遵循其自然順序的類
  • 不會持有碰撞的哈希表

而且,這與在流上調用sorted()的事實無關 - 它只是由於hashCode()的實現方式,因此也是哈希表的排序。 因此,問題的簡單答案是“不”。

暫無
暫無

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

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