java-recipes

ホーム Enum(列挙型) › En-04

En-04: Enum + Switch 文・Stream API での処理

Enum と Switch 式・Stream API を組み合わせた実用的な処理パターンを解説します。EnumSetEnumMap を活用すると、 Enum をキーにした処理が HashMap より高速かつ型安全になります。

いつ使うか

  • Enum の種類によって処理を分岐させたいとき(switch 式・Stream API)
  • Enum の特定の値だけを集めて操作したいとき(EnumSet)
  • Enum をキーとしてデータを管理したいとき(EnumMap)

EnumSet・EnumMap のパフォーマンスの特徴

クラス内部実装特徴
EnumSetビットベクトルHashSet より高速・null 不可
EnumMap配列(ordinal をインデックスに使用)HashMap より高速・キーに null 不可

サンプルコード

EnumSwitchStreamSample.java
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;

public class EnumSwitchStreamSample {

    enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;

        public boolean isWeekend() {
            return this == SATURDAY || this == SUNDAY;
        }
    }

    enum Priority {
        LOW, MEDIUM, HIGH, CRITICAL;
    }

    public static void main(String[] args) {
        // Java 8: 従来の switch 文
        Day day = Day.WEDNESDAY;
        String type;
        switch (day) {
            case MONDAY:
            case TUESDAY:
            case WEDNESDAY:
            case THURSDAY:
            case FRIDAY:
                type = "平日";
                break;
            default:
                type = "休日";
        }
        System.out.println(day + " は " + type);

        // EnumSet: 特定の Enum 値の集合(HashMap より高速)
        EnumSet<Day> weekdays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
        EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
        System.out.println("平日: " + weekdays);
        System.out.println("週末: " + weekend);

        // EnumSet で包含チェック
        System.out.println("MONDAY は平日?: " + weekdays.contains(Day.MONDAY));
        System.out.println("SUNDAY は平日?: " + weekdays.contains(Day.SUNDAY));

        // EnumMap: Enum をキーにした高速 Map
        EnumMap<Priority, List<String>> taskMap = new EnumMap<>(Priority.class);
        taskMap.put(Priority.HIGH, new ArrayList<>());
        taskMap.put(Priority.MEDIUM, new ArrayList<>());
        taskMap.put(Priority.LOW, new ArrayList<>());
        taskMap.get(Priority.HIGH).add("サーバー障害対応");
        taskMap.get(Priority.MEDIUM).add("定例ミーティング");
        taskMap.get(Priority.LOW).add("ドキュメント更新");

        // 全タスクを優先度順に表示
        System.out.println("\n=== タスク一覧(優先度順)===");
        for (Priority priority : Priority.values()) {
            List<String> tasks = taskMap.get(priority);
            if (tasks != null) {
                for (String task : tasks) {
                    System.out.println("[" + priority + "] " + task);
                }
            }
        }

        // for ループで Enum フィルタリング
        System.out.println("\n週末の日:");
        for (Day d : Day.values()) {
            if (d.isWeekend()) {
                System.out.println("  " + d);
            }
        }
    }
}

Java 8 では switch 文に break が必要です。case をまとめるには同じ case ラベルを連続して書きます(フォールスルー)。EnumSet・EnumMap は Java 5 から使えますが、Stream API は Java 8 からです。

よくあるミス・注意点

switch 文の break 忘れによるフォールスルー

Java 8 の switch 文では各 case に break が必要です。 break を忘れると次の case の処理も実行されます(フォールスルー)。 Java 14 以降の switch 式(-> 記法)ではフォールスルーが発生しないため、break が不要です。

EnumSet に null を追加すると NullPointerException が発生する

EnumSetEnumMap はいずれも null キーを許可しません。 null を追加しようとすると NullPointerException がスローされます。 null チェックが必要な場合は通常の HashSetHashMap を使いましょう。

Arrays.stream(values()) は毎回配列をコピーする

Enum.values() は呼び出すたびに新しい配列を生成します。 ループで繰り返し呼び出す場合はパフォーマンスに影響する可能性があります。 頻繁に参照する場合は static フィールドに保存しておく方法もあります。

switch 式(Java 14+)では全ケースを網羅しないとコンパイルエラー

switch 式で Enum を使う場合、全定数を列挙しないとコンパイルエラーになります。 これは安全性を高める仕組みで、後から Enum に定数を追加したとき、 switch 式を修正し忘れるとコンパイル時に気づけます。

テストする観点

  • switch 式で各 Day 定数を渡したとき、期待する文字列("平日" / "休日")が返ること
  • EnumSet.range(MONDAY, FRIDAY) が MONDAY〜FRIDAY の 5 要素を含むこと
  • EnumSet.of(SATURDAY, SUNDAY) が 2 要素を含み、MONDAY を含まないこと
  • EnumMap に登録したタスクが Priority の定義順(LOW → CRITICAL)で取得できること
  • Stream の filter で週末のみを取得したとき、SATURDAY・SUNDAY だけが返ること
  • isWeekend() が SATURDAY・SUNDAY で true、それ以外で false を返すこと(境界値: FRIDAY・SATURDAY)

GitHub でソースコードを見る →