コンテンツにスキップ

3-2. コレクションフレームワーク

コレクションフレームワークは、データのグループを効率的に管理するためのクラス群です。

コレクションフレームワークとは

配列の代わりに、より柔軟で機能的なデータ構造を提供します。

主要なインターフェース階層

Collection
├── List(順序あり、重複OK)
│   ├── ArrayList
│   ├── LinkedList
│   └── Vector
├── Set(順序なし、重複NG)
│   ├── HashSet
│   ├── LinkedHashSet
│   └── TreeSet
└── Queue(キュー)
    ├── PriorityQueue
    └── LinkedList

Map(キーと値のペア)
├── HashMap
├── LinkedHashMap
├── TreeMap
└── Hashtable

List(リスト)

順序を持ち、重複を許可するコレクションです。

ArrayList

動的配列の実装で、最も一般的に使用されます。

import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
    public static void main(String[] args) {
        // 宣言と初期化
        List<String> fruits = new ArrayList<>();

        // 要素の追加
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");
        fruits.add("Apple");  // 重複OK

        // 要素数
        System.out.println("Size: " + fruits.size());  // 4

        // 要素の取得
        String first = fruits.get(0);  // "Apple"

        // 要素の変更
        fruits.set(1, "Mango");

        // 要素の削除
        fruits.remove(0);  // インデックスで削除
        fruits.remove("Orange");  // 値で削除

        // 要素の存在確認
        boolean hasApple = fruits.contains("Apple");

        // インデックスの取得
        int index = fruits.indexOf("Mango");

        // 繰り返し
        for (String fruit : fruits) {
            System.out.println(fruit);
        }

        // クリア
        fruits.clear();
    }
}

LinkedList

双方向リンクリストの実装です。

import java.util.LinkedList;

LinkedList<String> list = new LinkedList<>();

// 先頭に追加
list.addFirst("First");

// 末尾に追加
list.addLast("Last");

// 先頭を取得・削除
String first = list.removeFirst();

// 末尾を取得・削除
String last = list.removeLast();

ArrayList vs LinkedList

ArrayList LinkedList
ランダムアクセス 高速(O(1)) 低速(O(n))
追加・削除(先頭) 低速(O(n)) 高速(O(1))
追加・削除(末尾) 高速(O(1)) 高速(O(1))
メモリ 効率的 オーバーヘッドあり
用途 一般的な用途 キュー、頻繁な挿入・削除

Set(セット)

重複を許可しないコレクションです。

HashSet

ハッシュテーブルを使った実装で、順序は保証されません。

import java.util.HashSet;
import java.util.Set;

Set<String> set = new HashSet<>();

// 要素の追加
set.add("Apple");
set.add("Banana");
set.add("Apple");  // 重複は追加されない

System.out.println(set.size());  // 2

// 要素の存在確認
boolean hasApple = set.contains("Apple");

// 要素の削除
set.remove("Banana");

// 繰り返し(順序は保証されない)
for (String item : set) {
    System.out.println(item);
}

LinkedHashSet

挿入順序を保持するHashSetです。

Set<String> set = new LinkedHashSet<>();
set.add("C");
set.add("A");
set.add("B");

// 挿入順で出力: C, A, B
for (String item : set) {
    System.out.println(item);
}

TreeSet

要素を自然順序(またはComparator)でソートして保持します。

import java.util.TreeSet;

TreeSet<Integer> set = new TreeSet<>();
set.add(5);
set.add(2);
set.add(8);
set.add(1);

// ソートされて出力: 1, 2, 5, 8
for (int num : set) {
    System.out.println(num);
}

// 最小値・最大値
int min = set.first();  // 1
int max = set.last();   // 8

Map(マップ)

キーと値のペアを格納します。

HashMap

ハッシュテーブルを使った実装で、最も一般的です。

import java.util.HashMap;
import java.util.Map;

Map<String, Integer> scores = new HashMap<>();

// 要素の追加
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.put("Charlie", 92);

// 要素の取得
int aliceScore = scores.get("Alice");  // 95

// 存在しないキー
Integer score = scores.get("David");  // null

// デフォルト値を指定
int davidScore = scores.getOrDefault("David", 0);  // 0

// 要素の存在確認
boolean hasAlice = scores.containsKey("Alice");
boolean has90 = scores.containsValue(90);

// 要素の削除
scores.remove("Bob");

// サイズ
int size = scores.size();

// キーの繰り返し
for (String name : scores.keySet()) {
    System.out.println(name + ": " + scores.get(name));
}

// 値の繰り返し
for (int score : scores.values()) {
    System.out.println(score);
}

// キーと値の繰り返し
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

LinkedHashMap

挿入順序を保持するHashMapです。

Map<String, Integer> map = new LinkedHashMap<>();
map.put("C", 3);
map.put("A", 1);
map.put("B", 2);

// 挿入順で出力: C=3, A=1, B=2

TreeMap

キーを自然順序でソートして保持します。

import java.util.TreeMap;

TreeMap<String, Integer> map = new TreeMap<>();
map.put("Charlie", 92);
map.put("Alice", 95);
map.put("Bob", 87);

// キーがソートされて出力: Alice=95, Bob=87, Charlie=92
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

Queue(キュー)

FIFO(First-In-First-Out)の原則で要素を処理します。

LinkedList(Queueとして)

import java.util.LinkedList;
import java.util.Queue;

Queue<String> queue = new LinkedList<>();

// 要素の追加(末尾)
queue.offer("First");
queue.offer("Second");
queue.offer("Third");

// 先頭を確認(削除しない)
String peek = queue.peek();  // "First"

// 先頭を取得・削除
String poll = queue.poll();  // "First"

// 空かどうか
boolean isEmpty = queue.isEmpty();

PriorityQueue

優先度に基づいて要素を取り出します。

import java.util.PriorityQueue;

PriorityQueue<Integer> pq = new PriorityQueue<>();

pq.offer(5);
pq.offer(2);
pq.offer(8);
pq.offer(1);

// 小さい順に取り出される
while (!pq.isEmpty()) {
    System.out.println(pq.poll());  // 1, 2, 5, 8
}

ユーティリティクラス

Collections

コレクションを操作する便利なメソッドを提供します。

import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);

// ソート
Collections.sort(numbers);  // [1, 2, 5, 8]

// 逆順
Collections.reverse(numbers);  // [8, 5, 2, 1]

// シャッフル
Collections.shuffle(numbers);

// 最大値・最小値
int max = Collections.max(numbers);
int min = Collections.min(numbers);

// 検索(ソート済みリスト)
Collections.sort(numbers);
int index = Collections.binarySearch(numbers, 5);

// 頻度
int count = Collections.frequency(numbers, 5);

// 不変リスト
List<String> immutable = Collections.unmodifiableList(numbers);

Arrays

配列とリストの変換などを提供します。

import java.util.Arrays;
import java.util.List;

// 配列 → リスト
String[] array = {"Apple", "Banana", "Orange"};
List<String> list = Arrays.asList(array);

// 注意: Arrays.asListは固定長(追加・削除不可)
// list.add("Mango");  // UnsupportedOperationException

// 可変リストに変換
List<String> mutableList = new ArrayList<>(Arrays.asList(array));
mutableList.add("Mango");  // OK

コレクションの選択ガイド

用途に応じた選択

用途 推奨
一般的なリスト ArrayList
頻繁な先頭への追加・削除 LinkedList
重複を排除 HashSet
ソートされたセット TreeSet
挿入順を保持するセット LinkedHashSet
キーと値のペア HashMap
ソートされたマップ TreeMap
キュー LinkedList
優先度付きキュー PriorityQueue

実践例: 単語の頻度カウント

import java.util.*;

public class WordFrequency {
    public static void main(String[] args) {
        String text = "apple banana apple orange banana apple";
        String[] words = text.split(" ");

        // 単語の頻度をカウント
        Map<String, Integer> frequency = new HashMap<>();

        for (String word : words) {
            frequency.put(word, frequency.getOrDefault(word, 0) + 1);
        }

        // 結果の表示
        for (Map.Entry<String, Integer> entry : frequency.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 出力:
        // apple: 3
        // banana: 2
        // orange: 1
    }
}

実践例: 学生管理システム

import java.util.*;

class Student {
    private String id;
    private String name;
    private int score;

    public Student(String id, String name, int score) {
        this.id = id;
        this.name = name;
        this.score = score;
    }

    public String getId() { return id; }
    public String getName() { return name; }
    public int getScore() { return score; }

    @Override
    public String toString() {
        return name + " (" + id + "): " + score;
    }
}

public class StudentManager {
    private Map<String, Student> students = new HashMap<>();

    public void addStudent(Student student) {
        students.put(student.getId(), student);
    }

    public Student getStudent(String id) {
        return students.get(id);
    }

    public List<Student> getAllStudents() {
        return new ArrayList<>(students.values());
    }

    public List<Student> getTopStudents(int n) {
        List<Student> all = getAllStudents();

        // スコアで降順ソート
        all.sort((s1, s2) -> Integer.compare(s2.getScore(), s1.getScore()));

        return all.subList(0, Math.min(n, all.size()));
    }

    public Map<String, Integer> getScoreDistribution() {
        Map<String, Integer> distribution = new TreeMap<>();

        for (Student student : students.values()) {
            int score = student.getScore();
            String grade;

            if (score >= 90) grade = "A";
            else if (score >= 80) grade = "B";
            else if (score >= 70) grade = "C";
            else if (score >= 60) grade = "D";
            else grade = "F";

            distribution.put(grade, distribution.getOrDefault(grade, 0) + 1);
        }

        return distribution;
    }

    public static void main(String[] args) {
        StudentManager manager = new StudentManager();

        manager.addStudent(new Student("001", "Alice", 95));
        manager.addStudent(new Student("002", "Bob", 87));
        manager.addStudent(new Student("003", "Charlie", 92));
        manager.addStudent(new Student("004", "David", 78));

        // トップ3を表示
        System.out.println("Top 3 Students:");
        for (Student student : manager.getTopStudents(3)) {
            System.out.println(student);
        }

        // 成績分布
        System.out.println("\nGrade Distribution:");
        for (Map.Entry<String, Integer> entry : manager.getScoreDistribution().entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}

まとめ

List

  • ArrayList: 一般的な用途、高速なランダムアクセス
  • LinkedList: 頻繁な挿入・削除

Set

  • HashSet: 重複排除、順序不要
  • TreeSet: ソートされたセット
  • LinkedHashSet: 挿入順を保持

Map

  • HashMap: 一般的なキー・値のペア
  • TreeMap: ソートされたマップ
  • LinkedHashMap: 挿入順を保持

Queue

  • LinkedList: 基本的なキュー
  • PriorityQueue: 優先度付きキュー

ユーティリティ

  • Collections: ソート、検索、最大・最小値など
  • Arrays: 配列とリストの変換

次のセクションでは、ジェネリクスについて学びます。