0Pricing
Java Academy · レッスン

EnumMap

enum をキーにする効率的なマップです。

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

EnumMap とは

EnumMapは、単一の列挙型の定数をキーとする特殊な Mapです。

内部では、定数の ordinal をインデックスとする通常の配列を基盤にしています。そのためハッシュ計算を行わず、検索と挿入を非常に高速に実行できます。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum Day { MON, TUE, WED }
    public static void main(String[] args) {
        Map<Day, String> plan = new EnumMap<>(Day.class);
        plan.put(Day.MON, "Gym");
        plan.put(Day.WED, "Swim");
        System.out.println(plan);
    }
}

構築にはクラスが必要

EnumMap はあらかじめキーの型を知る必要があるため、コンストラクターにClass オブジェクトを渡します。new EnumMap<>(Day.class)のように記述します。

これにより、定数の数に合わせて内部配列のサイズを決定できます。

import java.util.EnumMap;

public class Main {
    enum Priority { LOW, MEDIUM, HIGH }
    public static void main(String[] args) {
        EnumMap<Priority, Integer> counts = new EnumMap<>(Priority.class);
        counts.put(Priority.HIGH, 5);
        System.out.println(counts.get(Priority.HIGH));
    }
}

配列ベースの高速性

キーは array[ordinal]の位置に保存されるため、get と put は配列に直接アクセスします。HashMap のハッシュ計算やバケット走査より高速です。

キーのボックス化も、hashCode の呼び出しも発生しません。

import java.util.EnumMap;

public class Main {
    enum Slot { A, B, C, D }
    public static void main(String[] args) {
        EnumMap<Slot, Integer> m = new EnumMap<>(Slot.class);
        for (Slot s : Slot.values()) m.put(s, s.ordinal() * 10);
        System.out.println(m.get(Slot.C));
    }
}

キー順での反復

EnumMap は、キーの自然順序、つまり宣言順でエントリを反復します。

HashMap とは異なり、安定して予測可能な出力になります。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum Phase { PLAN, BUILD, SHIP }
    public static void main(String[] args) {
        Map<Phase, Integer> hours = new EnumMap<>(Phase.class);
        hours.put(Phase.SHIP, 3);
        hours.put(Phase.PLAN, 8);
        hours.put(Phase.BUILD, 20);
        for (Map.Entry<Phase, Integer> e : hours.entrySet()) {
            System.out.println(e.getKey() + " = " + e.getValue());
        }
    }
}

null キーは禁止

EnumMap ではnull キーを使用できません。null キーを指定すると NullPointerExceptionがスローされます。

ほかのマップと同様に、null 値は使用できます。

import java.util.EnumMap;

public class Main {
    enum K { A, B }
    public static void main(String[] args) {
        EnumMap<K, String> m = new EnumMap<>(K.class);
        m.put(K.A, null); // null value is fine
        System.out.println("A -> " + m.get(K.A));
        System.out.println("contains A: " + m.containsKey(K.A));
    }
}

EnumMap によるグループ化

よくある用途は、列挙型のカテゴリごとに集計またはグループ化することです。mergeを使うと集計を簡潔に記述できます。

import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class Main {
    enum Type { FRUIT, VEG, MEAT }
    record Item(String name, Type type) {}
    public static void main(String[] args) {
        List<Item> items = List.of(new Item("apple", Type.FRUIT), new Item("pear", Type.FRUIT), new Item("beef", Type.MEAT));
        Map<Type, Integer> counts = new EnumMap<>(Type.class);
        for (Item i : items) counts.merge(i.type(), 1, Integer::sum);
        System.out.println(counts);
    }
}

getOrDefault と computeIfAbsent

標準の Map のデフォルトメソッドはすべて使用できます。getOrDefaultは null チェックを不要にし、computeIfAbsentは値を遅延生成します。

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class Main {
    enum Group { A, B }
    public static void main(String[] args) {
        Map<Group, List<String>> m = new EnumMap<>(Group.class);
        m.computeIfAbsent(Group.A, k -> new ArrayList<>()).add("first");
        System.out.println(m);
        System.out.println(m.getOrDefault(Group.B, List.of()));
    }
}

コピーコンストラクター

EnumMap には、別の Map を受け取るコピーコンストラクターがあります。コピー元が EnumMap の場合は、そこからキーの型が推論されます。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum K { X, Y }
    public static void main(String[] args) {
        EnumMap<K, Integer> a = new EnumMap<>(K.class);
        a.put(K.X, 1);
        EnumMap<K, Integer> b = new EnumMap<>(a);
        b.put(K.Y, 2);
        System.out.println("copy: " + b);
    }
}

EnumMap と ordinal 配列

EnumMap が登場する前は、ordinal()をインデックスにする生の配列が使われていました。この方法は壊れやすく、安全性にも欠けます。

EnumMap なら、型安全性、境界安全性、使いやすい Map API を備えながら、同じ配列ベースの速度を得られます。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum Season { SPRING, SUMMER, FALL, WINTER }
    public static void main(String[] args) {
        Map<Season, String> mood = new EnumMap<>(Season.class);
        mood.put(Season.SUMMER, "sunny");
        // Safer and clearer than String[] indexed by ordinal
        System.out.println(mood.get(Season.SUMMER));
    }
}

ネストした EnumMap

EnumMap をネストして、2つの列挙型をキーとする行列をモデル化できます。たとえば、状態遷移表などに利用できます。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum State { SOLID, LIQUID, GAS }
    enum Transition { MELT, FREEZE }
    public static void main(String[] args) {
        Map<State, Map<Transition, State>> table = new EnumMap<>(State.class);
        Map<Transition, State> solid = new EnumMap<>(Transition.class);
        solid.put(Transition.MELT, State.LIQUID);
        table.put(State.SOLID, solid);
        System.out.println(table.get(State.SOLID).get(Transition.MELT));
    }
}

適切な型の選択

基本的な使い分けは次のとおりです。

  • キーが列挙型定数で、マップが必要な場合はEnumMapを使います。
  • 列挙型のセットが必要な場合はEnumSetを使います。
  • キーが任意のオブジェクトの場合は HashMap を使います。
import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum Level { DEBUG, INFO, WARN, ERROR }
    public static void main(String[] args) {
        Map<Level, Integer> thresholds = new EnumMap<>(Level.class);
        for (Level l : Level.values()) thresholds.put(l, l.ordinal() * 100);
        System.out.println(thresholds);
    }
}

クイックチェック

EnumMap についての知識を確認しましょう。

まとめ

EnumMap について学びました。

  • 列挙型定数をキーとする配列ベースの Map です。
  • コンストラクターにはキーの Classを渡し、null キーは使用できません。
  • 宣言順で反復します。
  • ordinal をインデックスとする配列を、安全かつ高速に置き換えられます。

これで高度な列挙型コースは完了です。

import java.util.EnumMap;

public class Main {
    enum K { A }
    public static void main(String[] args) {
        EnumMap<K, String> m = new EnumMap<>(K.class);
        m.put(K.A, "done");
        System.out.println("EnumMap course complete: " + m.get(K.A));
    }
}

よくある質問

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

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

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

enum をキーにする効率的なマップです。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

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

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

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

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

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

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

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

  1. フィールドとメソッドを持つ enum
  2. enum の抽象メソッド
  3. EnumSet
  4. EnumMap
← Java Academyに戻る