概要

Enum は定義して switch で分岐するだけでなく、EnumSet や EnumMap といった専用コレクション、Stream API との組み合わせで真価を発揮します。たとえば「平日だけの曜日セット」を EnumSet.range で一発で作れますし、「優先度ごとのタスク一覧」を EnumMap で管理すれば定義順のソートが自動で手に入ります。しかし Java 8 の switch 文と Java 14 以降の switch 式では書き方が大きく変わり、Stream の書き方にも差があるため、プロジェクトの Java バージョンに合わせた書き分けが必要です。この記事では、switch 文から switch 式への移行パターン、Stream で Enum をフィルタリング・変換する方法、EnumSet と EnumMap の使いどころを整理します。Java 21 のパターンマッチング switch で when ガードを使った条件分岐も含め、バージョンごとの書き方を対比します。

使いどころ

勤怠システムで EnumSet.range(MONDAY, FRIDAY) を平日セットとして定義し、打刻日が平日かどうかを contains で判定する

タスク管理ツールで EnumMap<Priority, List<Task>> を使い、優先度順に自動ソートされたタスク一覧を構築する

Stream で Enum.values() をフィルタリングし、特定条件に合致する区分値だけを画面のプルダウン選択肢として返す

コード例

EnumSwitchStreamExample.java
import java.util.Arrays;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;

public class EnumSwitchStreamExample {

    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) {
        // switch 式で曜日を分類(Java 14+)
        var day = Day.WEDNESDAY;
        String type = switch (day) {
            case MONDAY, TUESDAY, WEDNESDAY,
                 THURSDAY, FRIDAY -> "平日";
            case SATURDAY, SUNDAY -> "休日";
        };
        System.out.println(day + " は " + type);

        // EnumSet: 平日と週末を定義
        var weekdays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
        var weekend  = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
        System.out.println("平日: " + weekdays);
        System.out.println("週末: " + weekend);

        // Stream で週末の曜日をフィルタリング
        List<Day> weekendDays = Arrays.stream(Day.values())
            .filter(Day::isWeekend)
            .collect(Collectors.toList());
        weekendDays.forEach(d ->
            System.out.println("週末: " + d));

        // EnumMap: 優先度ごとのタスク一覧
        var taskMap = new EnumMap<Priority, List<String>>(
            Priority.class);
        taskMap.put(Priority.HIGH,
            List.of("サーバー障害対応"));
        taskMap.put(Priority.MEDIUM,
            List.of("定例ミーティング"));
        taskMap.put(Priority.LOW,
            List.of("ドキュメント更新"));

        // EnumMap は定義順で自動ソートされる
        System.out.println("\n=== タスク一覧(優先度順)===");
        for (var entry : taskMap.entrySet()) {
            entry.getValue().forEach(task ->
                System.out.println(
                    "[" + entry.getKey() + "] " + task));
        }
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

switch 式(-> 記法)で値を返せる。Stream + filter + collect で宣言的なフィルタリングが可能。var で型推論も利用可。

Java 17
// Java 17: switch 式 + Stream
var day = Day.WEDNESDAY;
String type = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY,
         THURSDAY, FRIDAY -> "平日";
    case SATURDAY, SUNDAY -> "休日";
};
// Stream でフィルタリング
List<Day> weekendDays = Arrays.stream(Day.values())
    .filter(Day::isWeekend)
    .collect(Collectors.toList());

Library Comparison

標準 API(EnumSet + EnumMap + Stream)Enum の分類・フィルタリング・マッピングを標準 API だけで完結させたいとき。コレクション操作の組み合わせが多いと可読性が下がることがある。適度にメソッド分割を行う。
Guava Sets / MapsEnumSet の差集合・交差集合など集合演算を多用するとき。Guava 依存が入る。EnumSet 自体が十分高速なので、集合演算だけのために導入するのは過剰な場合が多い。
Eclipse CollectionsEnum に限らず、コレクション操作全般を高性能な API で統一したいとき。学習コストとライブラリサイズが大きい。Enum 専用の用途なら標準 EnumSet/EnumMap で十分。

注意点

Java 8 の switch 文では break の書き忘れでフォールスルーが起きる。Java 14 以降の -> 記法なら break 不要でこの問題を回避できる

EnumSet.range は定義順の連続した範囲を取る。途中に要素が挿入されると範囲が変わるため、要素の定義順には注意を払うこと

EnumMap は Enum の定義順でエントリが並ぶ。表示順を定義順と一致させる設計にしておくと、別途ソートが不要になる

Arrays.stream(Enum.values()) は呼び出すたびに配列コピーが発生する。ループ内で繰り返し呼ぶ場合は事前にローカル変数にキャッシュする

Java 21 のパターンマッチング switch で when ガードを使う場合、ガード条件の評価順に注意。具体的なケースを先に書かないと到達不能コードになる

FAQ

EnumSet と HashSet のどちらを使うべきですか。

Enum をキーにする場合は常に EnumSet を選んでください。内部がビットフラグで実装されており、HashSet より高速かつ省メモリです。

EnumMap のエントリ順は保証されますか。

はい。EnumMap は Enum の定義順(ordinal 順)でエントリが並びます。表示順を Enum の定義順と合わせておけば、別途ソートは不要です。

switch 式で default を書くべきですか。

Enum を網羅する switch 式では default を書かないほうが安全です。要素追加時にコンパイルエラーで検出できるため、考慮漏れを防げます。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます