0Pricing
Java Academy · レッスン

EnumSet と EnumMap

効率的で型安全な列挙型ベースのコレクションに EnumSet と EnumMap を使用します。

「EnumSet と EnumMap」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

EnumSetとEnumMap

EnumSetとEnumMapは、enum向けに高度に最適化されたコレクション実装です。内部ではビットベクトルを使用するため、enumをキーとする場合はHashSetやHashMapよりも大幅に高速です。

EnumSet:セットの作成

EnumSetには、enum定数のセットを作成するためのファクトリメソッドが用意されています。

import java.util.*;

enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

EnumSet<Day> weekdays  = EnumSet.range(Day.MON, Day.FRI);
EnumSet<Day> weekend   = EnumSet.of(Day.SAT, Day.SUN);
EnumSet<Day> allDays   = EnumSet.allOf(Day.class);
EnumSet<Day> noDays    = EnumSet.noneOf(Day.class);

System.out.println(weekdays); // [MON, TUE, WED, THU, FRI]
System.out.println(weekend);  // [SAT, SUN]
System.out.println(allDays.size()); // 7

EnumSet:集合演算

EnumSetは、和集合、積集合、補集合といった標準的な集合演算をサポートします。ビットベクトルによる実装のため、これらの演算は非常に高速です。

import java.util.*;

enum Permission { READ, WRITE, DELETE, ADMIN, EXPORT }

EnumSet<Permission> userPerms  = EnumSet.of(Permission.READ, Permission.WRITE);
EnumSet<Permission> adminPerms = EnumSet.allOf(Permission.class);

// Complement
EnumSet<Permission> missing = EnumSet.complementOf(userPerms);
System.out.println("Missing: " + missing);
// Missing: [DELETE, ADMIN, EXPORT]

// Intersection (retain)
EnumSet<Permission> shared = EnumSet.copyOf(userPerms);
shared.retainAll(adminPerms);
System.out.println("Shared: " + shared); // [READ, WRITE]

EnumMap:enumから値へのマッピング

EnumMapは内部でenumのordinalを配列インデックスとして使用するため、検索はO(1)で、HashMapより高速です。

import java.util.*;

enum Quarter { Q1, Q2, Q3, Q4 }

EnumMap<Quarter, Double> revenue = new EnumMap<>(Quarter.class);
revenue.put(Quarter.Q1, 125_000.0);
revenue.put(Quarter.Q2, 148_500.0);
revenue.put(Quarter.Q3, 132_750.0);
revenue.put(Quarter.Q4, 175_200.0);

double total = revenue.values().stream()
    .mapToDouble(Double::doubleValue).sum();
System.out.printf("Annual revenue: $%.0f%n", total);
// Annual revenue: $581450

EnumMap:順序に従った反復処理

EnumMapは常に、キーをenumの宣言順に反復処理します。挿入順やハッシュ順ではありません。

import java.util.*;

enum Month { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }

EnumMap<Month, Integer> salesUnits = new EnumMap<>(Month.class);
salesUnits.put(Month.MAR, 1420);
salesUnits.put(Month.JAN, 980);
salesUnits.put(Month.FEB, 1105);

// Iterates JAN, FEB, MAR (declaration order, not insertion order)
for (var entry : salesUnits.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}
// JAN: 980
// FEB: 1105
// MAR: 1420

EnumSetを使った権限システム

効率的な権限チェックと整ったAPI設計を実現する、EnumSetを使ったロールベースの権限システムです。

import java.util.*;

enum Permission { READ, WRITE, DELETE, PUBLISH, ADMIN }

record Role(String name, EnumSet<Permission> permissions) {
    boolean can(Permission p) { return permissions.contains(p); }
}

Role editor = new Role("Editor",
    EnumSet.of(Permission.READ, Permission.WRITE, Permission.PUBLISH));
Role viewer = new Role("Viewer",
    EnumSet.of(Permission.READ));

System.out.println(editor.can(Permission.WRITE));  // true
System.out.println(viewer.can(Permission.DELETE)); // false

EnumMapを使った機能フラグ

EnumMapは機能フラグシステムに最適です。enumをキーとした高速なO(1)の検索を利用できます。

import java.util.*;

enum Feature { DARK_MODE, BETA_EDITOR, AI_SUGGESTIONS, EXPORT_PDF }

class FeatureFlags {
    private final EnumMap<Feature, Boolean> flags = new EnumMap<>(Feature.class);

    public FeatureFlags() {
        for (Feature f : Feature.values()) flags.put(f, false);
    }

    public void enable(Feature f)  { flags.put(f, true); }
    public void disable(Feature f) { flags.put(f, false); }
    public boolean isEnabled(Feature f) { return flags.getOrDefault(f, false); }
}

FeatureFlags ff = new FeatureFlags();
ff.enable(Feature.DARK_MODE);
System.out.println(ff.isEnabled(Feature.DARK_MODE));      // true
System.out.println(ff.isEnabled(Feature.AI_SUGGESTIONS)); // false

パフォーマンス:EnumSetとHashSet

EnumSetは、定数が64個以下のenumでは単一のlongビットマスクを使用するため、演算はO(1)のビット演算になります。HashSetはハッシュバケットを使用するため、より低速でオーバーヘッドも大きくなります。

import java.util.*;

enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

// EnumSet: bit operations on a long value
EnumSet<Day> workDays = EnumSet.range(Day.MON, Day.FRI);

// HashSet: hash table with boxing overhead
Set<Day> hashWorkDays = new HashSet<>(Arrays.asList(
    Day.MON, Day.TUE, Day.WED, Day.THU, Day.FRI));

// Both work, but EnumSet is ~5x faster for contains/add/remove
// and uses less memory
System.out.println(workDays.contains(Day.SAT));     // false
System.out.println(workDays.contains(Day.MON));     // true

EnumMapのgetOrDefaultとcomputeIfAbsent

EnumMapはgetOrDefault、computeIfAbsent、mergeなど、すべてのMap操作をサポートします。

import java.util.*;

enum Category { FOOD, ELECTRONICS, CLOTHING, BOOKS }

EnumMap<Category, List<String>> catalog = new EnumMap<>(Category.class);

// computeIfAbsent creates the list on first use
catalog.computeIfAbsent(Category.BOOKS, k -> new ArrayList<>()).add("Clean Code");
catalog.computeIfAbsent(Category.BOOKS, k -> new ArrayList<>()).add("Effective Java");
catalog.computeIfAbsent(Category.ELECTRONICS, k -> new ArrayList<>()).add("Laptop");

System.out.println(catalog.get(Category.BOOKS));
// [Clean Code, Effective Java]
System.out.println(catalog.getOrDefault(Category.FOOD, List.of()));
// []

EnumMapを使ったMultimapパターン

EnumMapとList型の値を組み合わせると、enumをキーとするMultimapを作成できます。カテゴリごとの項目のグループ化に効率的です。

import java.util.*;
import java.util.stream.*;

enum Priority { LOW, NORMAL, HIGH, URGENT }
record Task(String name, Priority priority) {}

List<Task> tasks = List.of(
    new Task("Fix login bug", Priority.URGENT),
    new Task("Update docs",   Priority.LOW),
    new Task("Add unit tests",Priority.NORMAL),
    new Task("Deploy hotfix", Priority.URGENT)
);

// Group by priority into EnumMap
EnumMap<Priority, List<Task>> byPriority = tasks.stream()
    .collect(Collectors.groupingBy(Task::priority,
        () -> new EnumMap<>(Priority.class),
        Collectors.toList()));

byPriority.forEach((p, ts) ->
    System.out.println(p + ": " + ts.stream().map(Task::name).collect(Collectors.joining(", "))));

EnumSetとEnumMapの使い分け

それぞれを選ぶ基準は次のとおりです:

  • EnumSet:enum値のセットが必要な場合 — 権限、アクティブな機能、選択された曜日など
  • EnumMap:各enum定数を値にマッピングする場合 — 機能ごとの設定、ランクごとのスコア、カテゴリごとの項目など
  • 両方:enumをキーにする場合はHashSetやHashMapよりも優先してください。高速でメモリ使用量が少なく、順序も維持されます

クイックチェック

定数が64個以下のenumに対して、EnumSetが内部で使用するデータ構造は何ですか。

復習:EnumSetとEnumMap

重要なポイント:

  • EnumSetはビットベクトルを使用するため、小さなenumのセットで非常に高速です
  • EnumSet.of()、range()、allOf()、noneOf()は一般的なファクトリメソッドです
  • EnumMapはordinalをインデックスとする配列を使用し、O(1)で検索でき、宣言順に並びます
  • 権限セット、機能フラグ、曜日によるフィルタリングにはEnumSetを使用します
  • カテゴリごとの設定、スコア表、機能から値へのマッピングにはEnumMapを使用します
  • どちらもenumの宣言順を維持し、ハッシュベースのコレクションより効率的です

よくある質問

「EnumSet と EnumMap」レッスンは無料ですか?

はい。「EnumSet と EnumMap」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「EnumSet と EnumMap」で何を学びますか?

効率的で型安全な列挙型ベースのコレクションに EnumSet と EnumMap を使用します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「EnumSet と EnumMap」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 列挙型の定義と使用
  2. フィールドとメソッドを持つ列挙型
  3. 列挙型を使った switch 式
  4. EnumSet と EnumMap
← Java Academyに戻る