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フィードバックを取得できます。ローカル設定は不要です。