CodeGym /コース /JAVA 25 SELF /新しいコレクション: SequencedCollection, SequencedSet, SequencedMa...

新しいコレクション: SequencedCollection, SequencedSet, SequencedMap

JAVA 25 SELF
レベル 34, レッスン 4
使用可能

1. 従来のコレクションにおける順序の問題

Java には、要素の順序を保証するコレクション(例: ArrayList、LinkedList、LinkedHashSet、LinkedHashMap)と、順序を保証しないコレクション(例: HashSet、HashMap)が昔から存在します。しかし、これらすべてのコレクションに共通の欠点がありました。つまり、いくつかのコレクションは特定の順序で要素を保持しているにもかかわらず、標準インターフェース(List、Set、Map)は先頭・末尾要素へのアクセスや順序の反転に関する汎用メソッドを提供していなかったのです。

例えば、List<String> があれば list.get(0) で先頭要素を取得できますが、Set や Map ではその手は使えません。イテレータを使ったり、余分なコードを書く必要がありました。これはあまり便利とは言えず、可読性も落ちます。

たとえるなら、引き出しのある棚があるのに、最初や最後の引き出しを取り出すたびに毎回すべて数え直さなければならないようなものです。専用の「最初」「最後」の取っ手があったらうれしいですよね。

SequencedCollection と関連インターフェースの登場

Java 21 で新しいコレクション・インターフェースが導入されました:

  • SequencedCollection<E>
  • SequencedSet<E>
  • SequencedMap<K, V>

これらのインターフェースは標準コレクションを拡張し、要素の順序を扱うための統一的なアプローチを導入します。これにより、順序が重要なあらゆるコレクションに対して、具体的な実装を気にせず汎用的なコードを書けるようになりました。

これはどういうものか

  • SequencedCollection — 要素に明確な順序があり、先頭・末尾の要素取得や順序の反転が簡単にできるコレクション。
  • SequencedSet — 同様だが集合(重複なし)に対するもの。
  • SequencedMap — 同様だがマップ(キーと値)に対するもの。

どのコレクションが実装しているか

Java 21 では、次の標準コレクションが新しいインターフェースを実装しています:

  • ArrayList、LinkedList → SequencedCollection
  • LinkedHashSet、TreeSet → SequencedSet
  • LinkedHashMap、TreeMap → SequencedMap

つまり、すでにこれらのコレクションを使っているなら、何もしなくても新しい機能を自動的に利用できます。

2. SequencedCollection、SequencedSet、SequencedMap の主なメソッド

SequencedCollection のメソッド

E getFirst();      // 先頭要素を取得
E getLast();       // 末尾要素を取得
SequencedCollection<E> reversed(); // 逆順のビューを取得

SequencedSet のメソッド

SequencedCollection と同じメソッドに加え、Set の機能すべてを備えます。

SequencedMap のメソッド

Map.Entry<K, V> firstEntry();    // 先頭のエントリ(キーと値)
Map.Entry<K, V> lastEntry();     // 末尾のエントリ(キーと値)
SequencedMap<K, V> reversed();   // 逆順の Map

3. 新しいインターフェースの使い方の例

例 1: 先頭と末尾の取得

import java.util.*;

public class SequencedDemo {
    public static void main(String[] args) {
        SequencedCollection<String> sc = new ArrayList<>();
        sc.add("Java");
        sc.add("Python");
        sc.add("Kotlin");

        // 先頭と末尾の要素を取得
        String first = sc.getFirst(); // "Java"
        String last = sc.getLast();   // "Kotlin"

        System.out.println("先頭: " + first);
        System.out.println("末尾: " + last);
    }
}

出力:

先頭: Java
末尾: Kotlin

例 2: コレクションの反転

SequencedCollection<String> sc = new LinkedList<>();
sc.add("A");
sc.add("B");
sc.add("C");

SequencedCollection<String> reversed = sc.reversed();
System.out.println(reversed); // [C, B, A]

注意: reversed() はコレクションを逆順に見せるビューを返します。元のコレクションを変更すると、reversed ビューも変更されます!

例 3: SequencedSet の利用

SequencedSet<Integer> set = new LinkedHashSet<>();
set.add(100);
set.add(200);
set.add(300);

System.out.println("先頭要素: " + set.getFirst()); // 100
System.out.println("末尾要素: " + set.getLast()); // 300

SequencedSet<Integer> reversedSet = set.reversed();
System.out.println(reversedSet); // [300, 200, 100]

例 4: SequencedMap の利用

SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.put("apple", 5);
map.put("banana", 3);
map.put("cherry", 7);

Map.Entry<String, Integer> first = map.firstEntry();
Map.Entry<String, Integer> last = map.lastEntry();

System.out.println("先頭: " + first.getKey() + " = " + first.getValue()); // apple = 5
System.out.println("末尾: " + last.getKey() + " = " + last.getValue()); // cherry = 7

SequencedMap<String, Integer> reversedMap = map.reversed();
System.out.println(reversedMap); // {cherry=7, banana=3, apple=5}

4. これがアプリケーションとどう関係するのか?

たとえば学習用アプリでログインしたユーザーの一覧を保持しており、「最初にログインした人」「最後にログインした人」を素早く取得したいとします。以前は次のように書く必要がありました:

List<String> users = new ArrayList<>();
// ... ユーザーを追加する
String first = users.get(0);
String last = users.get(users.size() - 1);

しかし、コレクションがリストではなく、例えば順序を保つ LinkedHashSet(要素は一意)である場合、この手は使えません:

Set<String> users = new LinkedHashSet<>();
// ... ユーザーを追加する
// 先頭をどう取得する? イテレータでしかできない:
String first = users.iterator().next();
// では末尾は? 全要素を走査するしかない!

今では、もっと簡単かつ汎用的に書けます:

SequencedSet<String> users = new LinkedHashSet<>();
// ... ユーザーを追加する
String first = users.getFirst();
String last = users.getLast();

これによりコード量が減るだけでなく、より読みやすく安全なコードになります。

5. 新しいインターフェースの関係図

classDiagram
    Collection <|-- SequencedCollection
    List <|-- SequencedCollection
    Set <|-- SequencedSet
    Map <|-- SequencedMap
    SequencedCollection <|-- SequencedSet
    SequencedSet <|-- LinkedHashSet
    SequencedSet <|-- TreeSet
    SequencedCollection <|-- ArrayList
    SequencedCollection <|-- LinkedList
    SequencedMap <|-- LinkedHashMap
    SequencedMap <|-- TreeMap

6. 役立つポイント

SequencedCollection の実用上の利点

  • 順序操作のための統一インターフェース: どこで get(0) が使えて、どこでイテレータが必要で、どこではそもそも先頭が取れないのか—覚える必要がなくなります。
  • キューやスタックの操作が容易: 先頭(head)と末尾(tail)を簡単に取得できます。
  • 安全性と可読性: コレクションの誤用によるバグが減り、コード自体が自己文書化的になります。
  • コレクションの素早い反転: reversed() により手作業なしで簡単に逆順が得られます。
  • 保守や拡張が容易: 将来、例えば ArrayList を LinkedHashSet に置き換えても、SequencedCollection を使うコードは書き換え不要です。

実装上の注意点

  • すべてのコレクションが SequencedCollection を実装するわけではない: 例えば HashSet や HashMap は順序を保証しないため、新しいインターフェースを実装しません。
  • メソッドは例外を投げることがある: コレクションが空のときに getFirst() や getLast() を呼ぶと NoSuchElementException が発生します。空でないことを確認しましょう!
  • reversed() はビューでありコピーではない: 元のコレクションの変更は逆順ビューにも反映され、逆もまた同様です。
  • 互換性: 新しいインターフェースは Java 21 以降でのみ利用できます。より古い Java を使っている場合はまだ利用できません(これを機にアップデートを検討しましょう)。
  • Generics: 新しいインターフェースは Generics を完全にサポートしており、あらゆるデータ型で利用できます。

7. SequencedCollection 利用時のよくあるミス

エラー1: 順序を保証しないコレクションに順序を期待してしまう。 HashSet を SequencedSet にキャストしようとするとコンパイルエラーになります。HashSet には順序がなく、このインターフェースを実装していないためです。

エラー2: 空のコレクションを無視する。 空のコレクションで getFirst() や getLast() を呼ぶと例外が投げられます。これらのメソッドを呼ぶ前に、コレクションが空でないことを確認してください:

if (!sc.isEmpty()) {
    String first = sc.getFirst();
}

エラー3: reversed() に関する誤解。 reversed() はビューでありコピーではありません。reversed ビューを変更すると元のコレクションも変更され(その逆も同様)、想定外の結果を生むことがあります。

エラー4: 古い Java バージョンで新しいインターフェースを使おうとする。 プロジェクトのコンパイル対象が Java 21 より古い場合、コンパイラはこれらのインターフェースを見つけられません。プロジェクト設定で JDK のバージョンを確認しましょう!

1
タスク
JAVA 25 SELF, レベル 34, レッスン 4
ロック未解除
スマートタスクディスパッチャー:処理前の空チェック 🤖
スマートタスクディスパッチャー:処理前の空チェック 🤖
1
タスク
JAVA 25 SELF, レベル 34, レッスン 4
ロック未解除
ステップのアルゴリズム:順方向と逆方向の操作 🔄
ステップのアルゴリズム:順方向と逆方向の操作 🔄
1
アンケート/クイズ
モダンなコレクション、レベル 34、レッスン 4
使用不可
モダンなコレクション
モダンなコレクションとイミュータビリティ
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION