ジェネリクスとコレクション完全攻略|Java Gold試験の「型安全」を徹底解説

Java Gold試験の最重要科目といえば「ジェネリクス」と「コレクション」です。 「ワイルドカード(?)が出てくると混乱する」「どのコレクションを選べばいいか迷う」という方も多いはず。

この記事では、試験で狙われるポイントを圧倒的なボリュームで徹底解説します。


目次

1. ジェネリクス(汎用)の正体

ジェネリクスとは、クラスやメソッドで扱う**「データ型」をパラメータとして渡せる仕組み**のことです。

なぜジェネリクスが「型安全」なのか?

ジェネリクスが導入される前のJavaでは、あらゆる型を Object 型として扱っていました。しかし、それには2つの大きな問題がありました。

  1. キャストの手間: 取り出すたびに (String) のようにキャストが必要。
  2. 実行時エラーのリスク: 違う型が混ざっていてもコンパイルで見抜けず、実行時に ClassCastException で落ちる。

ジェネリクスを使えば、不適切な型の代入をコンパイル時点でブロックできます。これが「型安全」の本質です。


2. 【最難関】境界ワイルドカードの完全理解

Java Goldで最も多くの受験生を悩ませるのが、extendssuper を使った境界ワイルドカードです。

PECSの法則(Producer-Extends, Consumer-Super)

試験攻略の鍵は、この「PECSの法則」を脳に刻むことです。

① <? extends T>:上限境界

「Tまたはそのサブクラス」を許容します。

  • 役割: データの「提供者(Producer)」。
  • 特徴: 読み取り専用。リストから取り出すことはできますが、新しく要素を add することはできません(型が特定できないため)。

② <? super T>:下限境界

「Tまたはその親クラス」を許容します。

  • 役割: データの「消費者(Consumer)」。
  • 特徴: 書き込み可能。T型(またはそのサブクラス)の要素を安全に add できます。

試験のひっかけパターン List<? extends Number> list = new ArrayList<Integer>(); list.add(10); // コンパイルエラー!
「IntegerはNumberを継承しているから追加できそう」と思わせるのが試験の罠です。extendsは「読み出し専用」と覚えましょう。


3. コレクションフレームワークの徹底比較

コレクションは、データの持ち方(データ構造)によって使い分けます。

List:順序と重複

  • ArrayList: 内部が配列。インデックス指定の検索が爆速(O(1))。
  • LinkedList: 要素同士がリンクで繋がっている。途中の挿入・削除に強い。

Set:重複排除とソート

  • HashSet: ハッシュ値で管理。順序はバラバラだが検索が非常に速い。
  • LinkedHashSet: 追加した順序を保持する。
  • TreeSet: **値を自動でソート(昇順)**して保持する。

Map:キーと値のペア

  • HashMap: キーの重複不可。非常に高速。
  • TreeMap: キーでソートされる。

4. 【新設】実務での使い分けとパフォーマンス

文字数アップと専門性向上のため、実務の視点を追加します。

試験では「TreeSetはソートされる」という知識で十分ですが、実務では**「ソートが必要なら最初からTreeSetを使うか、後でCollections.sort()するか」**という議論がよく出ます。

  • 大量のデータを扱う場合、毎回ソートが発生するTreeSetよりも、HashSetで集めてから最後にソートする方が速いケースもあります。


5. 実戦演習10問:クリックして回答をチェック!

SWELLの**「アコーディオンブロック」**を使用して作成しています。各「+」ボタンを押すと解答と解説が表示されます。

Q1:ジェネリクスの不変性

問題: 次のうち、コンパイルエラーになるものはどれか?

  1. List<Number> l = new ArrayList<Integer>();
  2. List<? extends Number> l = new ArrayList<Integer>();
  3. List<? super Integer> l = new ArrayList<Number>();
解答

解答:1 解説: Javaのジェネリクスは「不変」です。たとえIntegerがNumberを継承していても、List<Number>List<Integer> を入れることはできません。

Q2:PECSの法則(上限境界)

問題: List<? extends Number> list = new ArrayList<Integer>(); に対して list.add(10); を実行するとどうなるか?

解答

解答:コンパイルエラーになる 解説: extends(上限境界)は「読み取り専用」です。中身が何の型(IntegerかDoubleか)か特定できないため、null以外の追加は禁止されています。

Q3:TreeSetの特性

問題: TreeSet に “C”, “A”, “B”, “A” の順で追加した後の出力結果は?

解答

[A, B, C] 解説: Set なので重複した “A” は消え、TreeSet なので自然順序(昇順)に自動ソートされます。

Q4:HashMapの挙動

問題: HashMapput("key", "val1") した直後、同じキーで put("key", "val2") するとどうなるか?

解答

値が “val2” に上書きされる 解説: Mapはキーの重複を許しません。上書きされた際、put メソッドは「古い値(”val1″)」を戻り値として返します。

Q5:ジェネリクスメソッド

問題: 文法的に正しいジェネリクスメソッドの宣言は? A: public <T> T method(T t) B: public T <T> method(T t)

回答

A 解説: 型パラメータ <T> の宣言は、戻り値の型の「前」に置く必要があります。 [SWELLアコーディオン終了]

Q6:LinkedListのメリット

問題: リストの「中間」への要素挿入が最も高速なのは?

  1. ArrayList
  2. LinkedList
解答

2. LinkedList 解説: ArrayListは要素のシフト(移動)が発生しますが、LinkedListは参照の付け替えだけなので高速です。

Q7:Arrays.asListの制限

問題: Arrays.asList("A", "B").add("C"); を実行するとどうなるか?

解答

解答:UnsupportedOperationExceptionが発生する 解説: Arrays.asList で作られたリストは固定サイズです。要素の置換はできますが、追加や削除はできません。

Q8:下限境界ワイルドカード

問題: List<? super Integer> list = new ArrayList<Number>(); に対して list.add(10); は可能か?

解答

解答:可能 解説: super(下限境界)は「書き込み可能」です。Integerまたはそのサブクラスであれば安全に追加できます。

Q9:型消去(Type Erasure)

問題: ジェネリクスの型情報は実行時(Runtime)にも保持されるか?

解答

解答:保持されない(消去される) 解説: コンパイル時に型チェックが行われた後、型情報は削除されます(Object等に置換)。これを型消去と呼びます。

Q10:Dequeのスタック操作

問題: Deque をスタック(LIFO)として使うための適切なメソッドは?

解答

解答:push() と pop() 解説: Deque インターフェースは push で先頭追加、pop で先頭削除を行うことでスタック動作を実現します。


まとめ:合格へのチェックリスト

  • [ ] extends は読み出し専用、super は書き込み用
  • [ ] TreeSetTreeMap は「ソート」がキーワード
  • [ ] ListList のサブタイプではない(AがBを継承していても)

[参考リンク] より深い内部構造を知りたい方はOracle公式:コレクションの概要(外部リンク)を読み込みましょう。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

tibiyaのアバター tibiya ITエンジニア

文系卒あほエンジニア
趣味はゲームとギャンブルとテニスっぽいスポーツと釣りです
Java javascript angular react C#

コメント

コメントする

目次