[Java 21 の新機能] シーケンスされたコレクション

1 件の要約

定義された遭遇順序でコレクションを表すための新しいインターフェイスが導入されました。このような各セットには、明確に定義された最初の要素、2 番目の要素などがあり、最後の要素まで続きます。最初と最後の要素にアクセスし、その要素を逆の順序で処理するための統合 API を提供します。

「人生は後ろ向きにしか理解できないが、前向きに生きなければならない。」 - キェルケゴール

2 動機

Java コレクション フレームワークには、定義された遭遇順序を持つ要素のシーケンスを表すコレクション タイプがありません。また、これらのコレクションに適用される統一された操作セットもありません。これらのギャップは、問題や苦情の重大な原因となっています。

たとえば、List と Deque はどちらも遭遇順序を定義しますが、共通の親クラス Collection は遭遇順序を定義しません。同様に、Set は遭遇順序を定義せず、サブタイプ HashSet も定義しませんが、SortedSet や LinkedHashSet などのサブタイプは定義します。したがって、遭遇順序のサポートは型階層全体に分散されており、API で特定の有用な概念を表現することが困難になります。つまり、遭遇順序を持つパラメーターや戻り値をコレクション内に記述することができません。コレクションは一般的すぎるため、これらの制約を散文仕様に指定すると、デバッグが困難なエラーが発生する可能性があります。SortedSet と LinkedHashSet を除いて、リストが具体的すぎます。

よくある質問

ビュー コレクションは、多くの場合、より弱いセマンティクスへのダウングレードを強制されます。LinkedHashSet を Collections::unmodifiableSet でラップすると、順序情報を破棄する Set が生成されます。

それらを定義するインターフェイスはなく、遭遇順序に関連する操作は一貫性がないか、欠落しています。多くの実装は最初または最後の要素の取得をサポートしていますが、各コレクションは独自の方法を定義しており、いくつかは明白でない、または完全に欠落しています。

最初の要素 最後の要素
List list.get(0) list.get(list.size() - 1)
Deque deque.getFirst() deque.getLast()
SortedSet sortedSet.first() sortedSet.last()
LinkedHashSet linkedHashSet.iterator().next() // missing
  • リストの最後の要素を取得するなど、不必要に面倒なものもあります。
  • 勇気がなければ不可能なものもあります。LinkedHashSet の最後の要素を取得する唯一の方法は、セット全体を反復処理することです。
  • 同様に、最初の要素から最後の要素まで移動するには、通常、反復子または通常の for ループが必要となり、コードが長くなり、直感的ではなくなります。

これらの問題を解決するために、定義された遭遇順序を持つコレクションを表す新しいインターフェイス SequencedCollection が導入されました。各 SequencedCollection には、明確に定義された最初の要素、2 番目の要素、というように最後の要素までがあります。また、最初と最後の要素にアクセスし、その要素を逆の順序で処理するための統合 API も提供します。

SequencedCollection には、逆ソート ビューを提供する新しい reversed() メソッドも用意されています。このビューを使用すると、拡張された for ループ、明示的な iterator() ループ、forEach()、stream()、ParallelStream()、toArray() などの一般的な反復メカニズムをすべて使用して、コレクションが要素を逆の順序で処理できるようになります。

以前は LinkedHashSet から逆ソートされたストリームを取得するのが難しかった場合は、linkedHashSet.reversed().stream() だけが必要になります。

SequencedCollection では、両端での要素の追加、取得、削除をサポートするために、Deque のいくつかのメソッドも改善されています。

  • void addFirst(E)
  • void addLast(E)
  • E getFirst()
  • E getLast()
  • E removeFirst()
  • E removeLast()

add*(E) メソッドとremove*() メソッドはオプションであり、主に変更不可能なコレクションの場合をサポートします。コレクションが空の場合、get*() メソッドとremove*() メソッドは NoSuchElementException をスローします。

SequencedCollection のサブインターフェイスには矛盾する定義があるため、equals() メソッドと hashCode() メソッドは SequencedCollection では定義されていません。

これらの変更により、遭遇順序を持つコレクションの使用と操作が容易になり、これらのコレクションの要素を操作するための一貫した API が提供されます。

シーケンスセット

Set インターフェースの拡張であり、繰り返される要素のない順序付けられたコレクションです。

// since 21
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
    SequencedSet<E> reversed();    // 协变重写
}

SequencedSet インターフェイスは、反転した SequencedSet を返すために使用される reversed() メソッドを提供します。LinkedHashSet などのコレクションの場合、要素がコレクション内にすでに存在する場合は、適切な位置に移動されます。これにより、要素を再配置できないという LinkedHashSet の問題点が解決されます。

シーケンスマップ

Map インターフェイスの拡張であるエントリには、定義された走査順序があります。

interface SequencedMap<K,V> extends Map<K,V> {
    // 新方法
    SequencedMap<K,V> reversed();
    SequencedSet<K> sequencedKeySet();
    SequencedCollection<V> sequencedValues();
    SequencedSet<Entry<K,V>> sequencedEntrySet();
    V putFirst(K, V);
    V putLast(K, V);
    // 从NavigableMap中提升的方法
    Entry<K, V> firstEntry();
    Entry<K, V> lastEntry();
    Entry<K, V> pollFirstEntry();
    Entry<K, V> pollLastEntry();
}
  • reversed() は逆の順序で SequencedMap を返します
  • sequencedKeySet() は順序付けられたキーのセットを返します
  • sequencedValues() は、順序付けられた値のコレクションを返します。
  • sequencedEntrySet() は順序付けられたエントリのセットを返します

SequencedMap は、指定された場所にキーと値のペアを挿入するための putFirst() メソッドと putLast() メソッドも提供します。SortedMap などのマッピングの場合、これらのメソッドは UnsupportedOperationException をスローします。

SequencedMap は、両端でのエントリの取得と削除をサポートする NavigableMap インターフェイスからのいくつかのメソッドもプロモートします。

改造

新しいインターフェースを既存のクラスおよびインターフェース構造と統合するには、以下を調整します。

画像

  • List インターフェイスは SequencedCollection インターフェイスから直接継承するようになりました。
  • Deque インターフェースは SequencedCollection インターフェースから直接継承するようになりました。
  • LinkedHashSet クラスは SequencedSet インターフェイスを実装します
  • SortedSet インターフェースは SequencedSet インターフェースを直接継承するようになりました。
  • LinkedHashMap クラスは SequencedMap インターフェイスを実装します
  • SortedMap インターフェイスは SequencedMap インターフェイスを直接継承するようになりました。

必要に応じて、 reversed() メソッドの共変オーバーライドを提供します。たとえば、List#reversed() は、SequencedCollection 型の値ではなく List 型の値を返すように書き換えられます。

また、変更不可能なラッパーを作成するためのいくつかの新しいメソッドが Collections ユーティリティ クラスに追加されました。

  • Collections.unmodifiableSequencedCollection(sequencedCollection)
  • Collections.unmodifiableSequencedSet(sequencedSet)
  • Collections.unmodifiableSequencedMap(sequencedMap)

代替案

タイプ

List インターフェイスを一般的な順序付きコレクション型として再定義します。List は順序付けされていますが、整数インデックスを介した要素へのアクセスもサポートしています。順序付けされたデータ構造の多くは、本来はインデックス付けをサポートしていないため、反復によるインデックス付きアクセスをサポートする必要があります。これにより、インデックス付きアクセスのパフォーマンスが O(1) から O(n) に変化し、LinkedList のバグが永続化します。

Deque は、適切な一連の操作をすでにサポートしているため、一般的なシーケンス タイプとしては適切な選択であると思われます。ただし、これには、null を返す一連の操作 (オファー、ピーク、ポーリング) やキューから継承された操作など、他の操作も含まれています。これらの操作はキューでは適切ですが、他のコレクションではそれほど適切ではありません。Deque が一般的なシーケンス型として再定義されると、List も Queue になり、スタック操作をサポートすることになり、API が混乱してわかりにくくなります。

名前

ここで「シーケンス」という用語は、要素が特定の順序で配置されていることを意味するために選択されています。これは、同様のセマンティクスを持つコレクションを表すためにプラットフォーム間で広く使用されています。

「順序付けられた」という用語は十分具体的ではありません。要素を両方向に反復処理し、両端で操作する必要があります。Queue などの順序付きコレクションは明確な例外です。順序付けされていますが、明らかに非対称でもあります。

「リバーシブル」という用語は以前のバージョンでも使用されていましたが、それはすぐにダブルエンドの概念を呼び起こすものではありません。おそらく、より大きな問題は、Map バリアントの名前が ReversibleMap になることです。これは、キーと値による検索 (BiMap または BidiMap と呼ばれることもあります) をサポートしているかのように誤解を招くことを意味します。

追加して、追加して、UnsupportedOperationException

SortedSet の addFirst や SortedMap の putLast メソッドなど、相対比較によって順序が決定されるコレクションの場合、UnsupportedOperationException がスローされます。すべての SequencedCollection 操作を実装していない一部のコレクションの非対称性は、見た目が良くない可能性があります。ただし、これは SortedSet と SortedMap を順序付きセットのファミリーに加え、以前よりも広く使用できるようにするため、価値があります。この非対称性は、コレクションのフレームワークにおける以前のデザイン決定とも一致しています。たとえば、実際に返される実装が追加操作をサポートしていない場合でも、Map の keySet メソッドは Set を返します。

もう 1 つのアプローチは、インターフェイスを再配置することで追加操作を独立させておくことです。これにより、構造的に薄いセマンティクスを持ち、実際には役に立たず、型階層が乱雑になる可能性のある新しいインターフェイス型 (AddableCollection など) が生成されます。

テスト

包括的なテストのセットを JDK の回帰テスト スイートに追加します。

リスクと仮定

継承階層の上位に新しいメソッドを導入すると、reversed() や getFirst() などの明らかなメソッド名との競合が発生する可能性があります。

特に懸念されるのは、List および Deque の reversed() メソッドの共変オーバーライドです。これらのオーバーライドは、List および Deque を既に実装している既存のコレクションとソース コードおよびバイナリ互換性において互換性がありません。JDK には、このようなコレクションの例が 2 つあります。LinkedList と内部クラス sun.awt.util.IdentityLinkedList です。LinkedList クラスは、LinkedList 自体に reversed() の新しい共変オーバーライドを導入することでこれを処理します。内部 IdentityLinkedList クラスは不要になったため削除されました。

提案の以前のバージョンでは、SequencedMap インターフェイスの keySet()、values()、およびentrySet() メソッドの共変オーバーライドが導入されました。いくつかの分析の結果、このアプローチでは非互換性が生じるリスクが大きすぎることが判明し、実際、既存のサブクラスがすべて無効になってしまいました。既存のメソッドを共変オーバーライドに適応させる代わりに、SequencedMap に新しい sequencedKeySet()、sequencedValues()、および sequencedEntrySet() メソッドを導入するという代替アプローチが選択されました。振り返ってみると、これはおそらく、Java 6 で navigableKeySet() メソッドが導入されたときに、既存の keySet() メソッドを共変オーバーライドになるように変更するのではなく、同様のアプローチが取られたためだと思われます。

非互換性リスクの完全な分析については、CSR に添付されているレポート (JDK-8266572) を参照してください。

歴史

この提案は、2021 年の ReversibleCollections 提案を段階的に進化させたものです。この提案と比較すると、主な変更点は、名前の変更、SequencedMap インターフェイスの導入、および変更不可能なラッパー メソッドの導入です。

ReversibleCollection 提案は、Tagir Valeev の 2020 OrderedMap/OrderedSet 提案に基づいています。提案の基本概念の一部は残りますが、詳細は大きく異なります。

長年にわたり、リストとセットまたはマップを組み合わせるための多くのリクエストや提案を受けてきました。これらのリクエストには、4152834、4245809、4264420、4268146、6447049、および 8037382 が含まれます。

これらのリクエストの一部は、Java 1.4 で導入された LinkedHashSet および LinkedHashMap で部分的に満たされます。これらのクラスはいくつかのユースケースを満たしますが、上で説明したように、その導入によりコレクション フレームワークの抽象化と操作にギャップが残ります。

テスト

JDK の回帰テスト スイートに包括的なテスト セットを追加する予定です。

リスクと仮定

reversed() や getFirst() などの明らかなメソッド名など、新しいメソッドを継承階層に導入する場合、競合が発生するリスクがあります。特に興味深いのは、List と Deque の共変オーバーライドの reversed() メソッドです。これらのメソッドはソース コードであり、List と Deque を既に実装している既存のコレクションとはバイナリ互換性がありません。JDK には、このようなコレクションの例が 2 つあります。LinkedList と内部クラス sun.awt.util.IdentityLinkedList です。LinkedList クラスは、LinkedList 自体に新しい reversed() 共変オーバーライドを導入することでこれを処理します。内部 IdentityLinkedList クラスは不要になったため削除されました。提案の以前のバージョンでは、SequencedMap インターフェイスの keySet()、values()、およびentrySet() メソッドに共変オーバーライドが導入されました。いくつかの分析の結果、このアプローチは非互換性のリスクが大きすぎることが判明し、本質的に既存のサブクラスが無効になることが判明しました。既存のメソッドを共変オーバーライドに適応させる代わりに、SequencedMap に新しいメソッド sequencedKeySet()、sequencedValues()、および sequencedEntrySet() を導入するという代替案が選択されました。振り返ってみると、Java 6 で navigableKeySet() メソッドが導入されたときに、既存の keySet() メソッドを共変オーバーライドに変更する代わりに、同様のアプローチが取られたのもおそらく同じ理由でした。非互換性リスクの完全な分析については、CSR に添付されているレポート (JDK-8266572) を参照してください。

参考

この記事は、複数の記事を公開するブログOpenWriteによって公開されています。

ブロードコム、既存のVMwareパートナープログラム終了を発表 . サイトBが2度クラッシュ、テンセントの「3.29」レベル1インシデント…2023年のダウンタイムインシデントトップ10を棚卸し、 Vue 3.4「スラムダンク」リリース、 ヤクルトが95Gデータ流出を確認 MySQL 5.7、Moqu、Li Tiaotiao... 2023 年に「停止」される (オープンソース) プロジェクトと Web サイトを棚卸す 「2023 中国オープンソース開発者レポート」が正式リリース 30 年前の IDE を振り返る: のみTUI、明るい背景色…… Julia 1.10が正式リリース Rust 1.75.0がリリース NVIDIAがGeForce RTX 4090 Dを中国で特別販売開始
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/3494859/blog/10555187