導入:なぜ今、SequencedCollectionなのか
Javaのコレクションフレームワークは長年、ListやDequeといったインターフェースごとに操作方法が分断されていました。例えば、リストの先頭を取得するのに「get(0)」を使い、Dequeでは「getFirst()」を使うなど、一貫性に欠けていました。Java 21で導入された「SequencedCollection」は、これらのコレクションに対して、先頭・末尾へのアクセスや反転といった操作を共通化し、コードの可読性と保守性を劇的に向上させるための重要な改善です。
基礎知識:SequencedCollectionとは
SequencedCollectionは、要素に明確な「遭遇順序(encounter order)」があるコレクションを扱うためのインターフェースです。List、Deque、LinkedHashSetなどがこのインターフェースを継承しています。
これまで、特定の順序を持つコレクションを操作する際に、キャストしたり、特定の型に依存したメソッドを呼び出す必要がありましたが、これからはSequencedCollectionインターフェースを通じて、一貫したAPIを利用できるようになります。
実装と解決策:主要メソッドの活用
SequencedCollectionが提供する主なメソッドは以下の通りです。
・addFirst(E e) / addLast(E e): 先頭または末尾に要素を追加。
・getFirst() / getLast(): 先頭または末尾の要素を取得。
・removeFirst() / removeLast(): 先頭または末尾の要素を削除して取得。
・reversed(): 要素を逆順に反転させたビューを返す。
これらを使うことで、これまで「list.add(0, e)」のようにインデックスを意識して記述していた操作が、より直感的かつ安全に行えるようになります。
サンプルプログラム
以下のコードは、ArrayListをSequencedCollectionとして扱い、各操作を検証する例です。
import java.util.ArrayList;
import java.util.List;
public class SequencedCollectionExample {
public static void main(String[] args) {
// ArrayListはListインターフェースを介してSequencedCollectionとして機能します
List<String> list = new ArrayList<>();
// 1. 要素の追加
list.addLast("B");
list.addFirst("A");
list.addLast("C");
System.out.println("初期状態: " + list); // [A, B, C]
// 2. 先頭と末尾の取得
System.out.println("先頭: " + list.getFirst()); // A
System.out.println("末尾: " + list.getLast()); // C
// 3. 削除操作
list.removeFirst();
System.out.println("先頭削除後: " + list); // [B, C]
// 4. 逆順ビューの取得
// 元のリストには影響を与えず、逆順にイテレート可能なビューを返します
System.out.print("逆順表示: ");
list.reversed().forEach(s -> System.out.print(s + " "));
}
}
応用・注意点:現場での活用と落とし穴
1. 逆順ビューの性質を理解する
reversed()メソッドが返すのは、新しいリストをコピーしたものではなく、元のコレクションへの「ビュー(見え方)」です。したがって、reversed()で取得したリストに対して変更を加えると、元のコレクションにも反映されます。意図しない副作用を避けるため、操作には注意してください。
2. 既存コードとの互換性
SequencedCollectionはインターフェースの階層構造に追加されたため、既存のListやDequeを実装しているクラスは、自動的にこの機能を利用可能です。ただし、LinkedHashSetなど、順序を保持するSet系コレクションを扱う際に、これまで「get」系メソッドがなかったものにアクセスできるようになるため、設計の幅が広がります。
3. Nullの扱い
getFirst()やgetLast()は、コレクションが空の場合に「NoSuchElementException」をスローします。実務では、事前にisEmpty()やsize()でチェックを行うか、Optionalの利用を検討するなどの例外処理を忘れないようにしましょう。

コメント