Java 21 中的有序集合
1. 概述
Java 21預計將於 2023 年 9 月發布,成為繼 Java 17 之後的下一個長期支援版本。在新功能中,我們可以看到 Java collections framework
的更新,稱為Sequenced Collections 。
序列集合提案作為一項改變遊戲規則的增強功能脫穎而出,有望重新定義開發人員與集合互動的方式。此功能將新介面注入現有層次結構,提供無縫機制來使用內建預設方法存取集合的第一個和最後一個元素。此外,它還支援獲取集合的反向視圖。
在本文中,我們將探討這項新的增強功能、其潛在風險以及它所帶來的優勢。
2. 動機
缺乏具有定義的遭遇順序的集合的通用超類型一直是問題和抱怨的根源。此外,缺乏存取第一個和最後一個元素以及以相反順序迭代的統一方法一直是 Java collections framework
的長期限制。
我們可以以List
和Deque
為例:兩者都定義了遇到順序,但它們的公共超類型Collection
沒有定義。同樣, Set
不定義遇到順序,但某些子類型(例如SortedSet
和LinkedHashSet
)定義了遇到順序。因此,遭遇順序的支援分佈在類型層次結構中,與遭遇順序相關的操作要麼不一致,要麼缺失。
為了示範這種不一致,我們來比較一下存取不同集合類型的第一個和最後一個元素:
訪問第一個元素 | 訪問最後一個元素 | |
List | list.get(0) | list.get(list.size() – 1) |
Deque | deque.getFirst() | deque.getLast() |
SortedSet | sortedSet.first() | sortedSet.last() |
LinkedHashSet | linkedHashSet.iterator().next() | // missing |
當嘗試取得集合的反向視圖時,也會發生相同的情況。雖然從第一個元素到最後一個元素迭代集合的元素遵循清晰一致的模式,但以相反的方向這樣做會帶來挑戰。
為了說明這一點,在處理NavigableSet
時,我們可以使用descendingSet()
方法。對於Deque
, descendingIterator()
方法被證明是有用的。類似地,在處理List
時, listIterator()
方法效果很好。但是, LinkedHashSet
的情況並非如此,因為它不提供任何反向迭代支援。
所有這些差異導致了程式碼庫的碎片化和複雜性,使得在 API 中表達某些有用的概念變得具有挑戰性。
3. 新的Java集合層次結構
此新功能引入了用於排序集合、排序集和排序映射的三個新接口,這些接口已添加到現有的集合層次結構中:
此圖片是JEP 431 官方文件的一部分:序列化集合(來源)。
3.1. SequencedCollection
有序集合是其元素具有定義的遇到順序的Collection
。新的SequencedCollection
介面提供了在集合兩端新增、檢索或刪除元素的方法,以及取得集合的逆序視圖的方法。
interface SequencedCollection<E> extends Collection<E> {
// new method
SequencedCollection<E> reversed();
// methods promoted from Deque
void addFirst(E);
void addLast(E);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
}
除reversed()
之外的所有方法都是預設方法,提供預設實現,並且從Deque
提升。 reversed()
方法提供原始集合的逆序視圖。此外,對原始集合的任何修改都可以在反向視圖中看到。
add*()
和remove*()
方法是可選的,並在其預設實作中拋出UnsupportedOperationException
,主要是為了支援不可修改的集合和具有已定義排序順序的集合的情況。如果集合為空, get*()
和remove*()
方法將拋出NoSuchElementException
。
3.2. SequencedSet
序列集可以定義為專門的Set
,其功能類似SequencedCollection
,確保不存在重複元素。 SequencedSet
介面擴充了SequencedCollection
並重寫了其reversed()
方法。唯一的差別是SequencedSet.reversed()
的回傳類型是SequencedSet
。
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
// covariant override
SequencedSet<E> reversed();
}
3.3. SequencedMap
排序映射是其條目具有定義的遭遇順序的Map
。 SequencedMap
不會擴充SequencedCollection
,並提供自己的方法來操作集合兩端的元素。
interface SequencedMap<K, V> extends Map<K, V> {
// new methods
SequencedMap<K, V> reversed();
SequencedSet<K> sequencedKeySet();
SequencedCollection<V> sequencedValues();
SequencedSet<Entry<K, V>> sequencedEntrySet();
V putFirst(K, V);
V putLast(K, V);
// methods promoted from NavigableMap
Entry<K, V> firstEntry();
Entry<K, V> lastEntry();
Entry<K, V> pollFirstEntry();
Entry<K, V> pollLastEntry();
}
與SequencedCollection
類似, put*()
方法對於不可修改的映射或具有已定義排序順序的映射拋出UnsupportedOperationException
。此外,在空地圖上呼叫從NavigableMap
提升的方法之一會導致拋出NoSuchElementException
。
4.風險
新介面的引入應該不會影響僅使用集合實現的程式碼。但是,如果在我們的程式碼庫中定義自訂集合類型,可能會出現幾種衝突:
- 方法命名:引入的新方法可能會與現有類別上的方法發生衝突。例如,如果我們有一個
List
介面的自訂實現,該實作已經定義了getFirst()
方法,但傳回類型與SequencedCollection
中定義的getFirst()
不同,那麼升級到 Java 21 時將會導致來源不相容。 - 協變重寫:
List
和Deque
都提供了reversed()
方法的協變重寫,一個回傳List
,另一個回傳Deque
。因此,任何實作這兩個介面的自訂集合在升級到 Java 21 時都會導致編譯時錯誤,因為編譯器無法選擇其中一個。
報告JDK-8266572包含對不相容風險的完整分析。
5. 結論
總之,順序集合標誌著 Java 集合的重大飛躍。透過滿足長期以來對以統一方式處理具有定義的遇到順序的集合的需求,Java 使開發人員能夠更有效率、更直觀地工作。新介面建立了更清晰的結構和一致的行為,從而產生更健壯和可讀的程式碼。
與往常一樣,原始碼可以在 GitHub 上取得。