Java Goldへの道:紫本「コレクションとジェネリックス」学習備忘録

はじめに

記事は”Oracle Certified Java Programmer, Gold SE 11認定資格(Java Gold SE11)”合格に向けた学習備忘録のまとめです。

< この学習備忘録について >
内容や重要な点が抜けていた場合には適宜更新していきます

Java Gold合格において重要な単元

Java Goldの学習においては、下記の単元が非常に重要となってきます。

  • 関数型インタフェース
  • ラムダ式
  • JavaストリームAPI
  • 並列処理

ですが、今回は上記にはないものの、これまた重要な箇所であるコレクションとジェネリックスについて取り上げたいと思います。

私と同じようにJava Goldの合格を目指して勉強されている皆様にとって、少しでもお役に立てれば幸いです。

List, Set, Map:コレクションの三大インタフェースと実装クラス

Javaのコレクションフレームワークは、データを効率的に扱うための仕組みです。その中心となるのがList, Set, Mapの3つの主要なインタフェースです。それぞれの特徴と、代表的な実装クラスを整理します。

インタフェース特徴代表的な実装クラス
List順序を保持し、要素の重複を許容する。インデックスで要素を管理。ArrayList
内部的に配列でデータを保持。要素へのアクセスが高速
LinkedList
要素が数珠つなぎ(双方向リスト)でデータを保持。要素の追加・削除が高速
Set順序を保持せず、要素の重複を許容しない。一意な要素の集合。HashSet
hashCode()メソッドで要素を管理。高速だが順序は保証されない
TreeSet
要素を自動でソートして格納
(自然順序付け or Comparator)
Mapキー(Key)と値(Value)のペアでデータを管理。キーの重複は許容しないHashMap
hashCode()でキーを管理。高速だが順序は保証されない
TreeMap
キーを自動でソートして格納。
  • 「重複データも順番通りに格納したい」ならArrayList
  • 「ユニークな値だけを高速に扱いたい」ならHashSet
  • 「IDと名前のようにペアで管理したい」ならHashMap
    といったように、実現したいことに合わせて最適なコレクションを選択することが基本になります。

【深掘り】MapインタフェースとPropertiesクラス

Mapの要素を効率的に処理するentrySet()

Mapに格納されたすべてのキーと値のペアを処理したい場合、entrySet()メソッドを使うのが最も効率的で標準的な方法です。
map.entrySet()を呼び出すと、Map内の各エントリ(キーと値のペア)がMap.Entry<K, V>オブジェクトとして格納されたSetが返されます。このSetを拡張for文でループさせることで、各エントリからキーと値を個別に取り出すことができます。

Map<Integer, String> map = new HashMap<>();
map.put(1, "Apple");
map.put(2, "Banana");

// entrySet()を使ってMapの全要素をループ処理
for (Map.Entry<Integer, String> entry : map.entrySet()) {
    Integer key = entry.getKey();
    String value = entry.getValue();
    System.out.println("Key: " + key + ", Value: " + value);
}

Propertiesクラスとget / getPropertyメソッドの違い

Mapの中でも特殊な実装クラスとしてPropertiesクラスがあります。これは主に設定ファイル(.propertiesファイル)の読み書きに使われ、キーと値を常にString型として扱うという特徴があります。
PropertiesHashtableのサブクラスでありMapインタフェースを実装しているため、getメソッドも使えますが、Propertiesクラス固有のgetPropertyメソッドも持っています。この2つには重要な違いがあります。

メソッドget(Object key)getProperty(String key)
所属MapインタフェースPropertiesクラス
特徴Mapの汎用的な値取得メソッドStringのキーでStringの値を取得するProperties専用メソッド
キーが存在しない場合nullを返すnullを返す
ただし、デフォルト値を指定できるオーバーロード版がある。

コードで見る違い
import java.util.Properties;

public class PropertiesExample {
    public static void main(String[] args) {
        Properties props = new Properties();
        props.setProperty("db.user", "myuser");
        
        // getPropertyの便利な使い方:デフォルト値の指定
        String pass = props.getProperty("db.pass", "default_pass"); // "default_pass"

        System.out.println("db.pass: " + pass);
    }
}

getProperty(key, defaultValue)は、キーが存在しなかった場合にnullを返す代わりにデフォルト値を返すことができます。これにより、NullPointerExceptionを避け、より安全で簡潔なコードを書くことが可能になります。

ListとQueue:インタフェース型で変わるremoveメソッドの挙動

LinkedListクラスはListインタフェースとQueueインタフェースの両方を実装しています。そのため、どちらの型の変数でインスタンスを扱うかによって、同じremoveメソッドでも挙動が異なります。

// Listとして扱った場合
List<Integer> list = new LinkedList<>();
list.add(100); list.add(200);
list.remove(1); // List<E>の E remove(int index) が呼ばれる
System.out.println(list); //-> 出力結果:[100]

// Queueとして扱った場合
Queue<Integer> queue = new LinkedList<>();
queue.add(100); queue.add(200);
queue.remove(1); // Queue<E>の boolean remove(Object o) が呼ばれる
System.out.println(queue); //-> 出力結果:[100, 200]

Listとして扱った場合、removeメソッドはインデックスを引数に取るため、インデックス1の要素(200)が削除されます。
一方、Queueとして扱った場合、removeメソッドはオブジェクトを引数に取ります。このコードではIntegerオブジェクトの1という値を持つ要素を探しますが、存在しないため何も削除されません。
同じインスタンスでも、どのインタフェース型として扱うかによって呼び出されるメソッドのシグネチャが異なる、というポリモーフィズムの重要なポイントです。

Comparable vs Comparator:役割とTreeSetにおける優先順位

オブジェクトの順序付けを行うComparableComparator
それぞれの役割とパッケージの違いは明確に覚えておきたいポイントです。
TreeSetは、コンストラクタでComparatorが指定されていればそれを優先し、指定がなければ要素のComparable(自然順序付け)を利用します。

Comparable (java.langパッケージ): オブジェクトの「自然順序付け」

そのオブジェクト自身が持つ、本質的な比較ルールを定義します。(compareToメソッド)

Comparator (java.utilパッケージ): オブジェクトの「外部からの順序付け」

オブジェクトの自然順序付けとは異なる基準でソートしたい場合に、比較ルールを外部から定義します。(compareメソッド)

【深掘り】ComparableとComparatorの技術的な違い

さらに、両者の技術的な違いをメソッドのシグネチャと戻り値の観点から詳しく見ていきましょう。

Comparable<T> インタフェース
  • メソッド:int compareTo(T other)
  • 戻り値のルール
    ・負の値:this(自身)がother(引数)より小さい(前に来る)場合
    ・ゼロ:this(自身)とother(引数)が等しい場合
    ・正の値: this(自身)がother(引数)より大きい(後に来る)場合
  • オーバーライド記法
    Comparableを実装する場合、クラス定義でインタフェースを実装し、クラス内でcompareToメソッドをオーバーライドします。
// 学生番号で並び替えるStudentクラス
public class Student implements Comparable<Student> {
    private int studentId;

    // ... コンストラクタなど ...

    // compareToメソッドをオーバーライド
    @Override
    public int compareTo(Student other) {
        // 自分自身(this)の学生番号と、比較対象(other)の学生番号を比較
        return this.studentId - other.studentId; // 昇順ソート
    }
}

Comparator<T> インタフェース
  • メソッド:int compare(T other1, T other2)
  • 戻り値のルール
    ・負の値:other1(第一引数)がother2(第二引数)より小さい(前に来る)場合
    ・ゼロ:other1(第一引数)がother2(第二引数)が等しい場合
    ・正の値: other1(第一引数)がother2(第二引数)より大きい(前に来る)場合
  • オーバーライド記法(クラス作成)
    Comparatorのロジックを再利用したい場合、専用のクラスを作成してcompareメソッドをオーバーライドします。
// 学生の名前で並び替える、専用のComparatorクラス
public class StudentNameComparator implements Comparator<Student> {
    // compareメソッドをオーバーライド
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getName().compareTo(s2.getName());
    }
}

  • オーバーライド記法(ラムダ式)
    一度しか使わないような簡単な比較ロジックの場合、ラムダ式を使ってその場でComparatorを実装するのが現代的で簡潔な記法です。
List<Student> students = new ArrayList<>();
// ... studentsに要素を追加 ...

// ラムダ式を使い、その場でComparatorを実装して名前順でソートする
students.sort((s1, s2) -> s1.getName().compareTo(s2.getName()));

このラムダ式(s1, s2) -> s1.getName().compareTo(s2.getName())は、Comparatorインタフェースのcompareメソッドの実装そのものです。

ジェネリックスの型ルール整理

型安全性を保証するジェネリックスの、重要なシンタックスとルールを整理します。

インスタンス化:Raw型とダイヤモンド演算子
// Raw型:型引数を省略した書き方。非推奨。
new Foo(true);

// ダイヤモンド演算子:コンパイラによる型推論。推奨される書き方。
new Foo<>(10);

ワイルドカードと非変性(Invariance)

ジェネリックスでは、たとえ継承関係にあっても、異なる型引数を持つクラス同士に互換性はありません。これを「非変性(Invariance)」と呼びます。

// StringはObjectのサブクラスだが、これはコンパイルエラーになる
List<Object> obj = new ArrayList<String>(); 

これは、List<Object>IntegerなどString以外のオブジェクトが追加されるのを防ぎ、型安全性を保つための重要なルールです。

まとめ

今回は「コレクションとジェネリックス」について、学習した重要ポイントを備忘録として整理しました。

  • List, Set, Mapの特性を理解し、用途に応じて使い分ける
  • インタフェースの型がメソッドの挙動を決定する
  • ComparableとComparatorの役割と優先順位
  • ジェネリックスの型安全性に関する厳密なルール

これらの基本原則をしっかり理解しておくことが、応用的な問題を解く上での土台になると感じています。この記事が、同じように学習している皆さんの知識の整理に役立てば幸いです!

櫟原侑祐

 櫟原侑祐

スキル:Java/SpringBoot/Spring/Kotlin/Bootstrap/Thymeleaf/HTML/CSS/JavaScript/TypeScript/Angular/MySQL/PostgreSQL/MongoDB/Eclipse/Spring Tool Suite4/Android Studio/A5:SQL Mk-2/MongoDB Compass/GitHub

コメント

この記事へのコメントはありません。

関連記事