1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Effective Java 6.37:序数インデックスの代わりに EnumMap を使う

Posted at

6.37:序数インデックスの代わりに EnumMap を使う

要点

列挙型(enum)をキーにして何かを管理するときは、ordinal() を使って配列/序数インデックスで管理するより EnumMap<YourEnum, V> を使う — 型安全で可読性が上がり、内部実装は効率的(実質的に配列ベース)なので性能面でも優れる。

なぜ EnumMap を使うべきか

  • 型安全・可読性:
    map.get(Planet.EARTH) の方が arr[Planet.EARTH.ordinal()] より読みやすく、間違えにくい。
  • 実装効率が高い:
    EnumMap は内部的に enum.ordinal() をベースに配列で実装されており、HashMap より軽量で高速(O(1))。
  • 保守性:
    ordinal() に直接依存するコードは enum の並べ替えで壊れるが、EnumMap ならキーは enum 値そのものなので順序変更の影響を受けない(※永続化は別途考える)。
  • 標準 API の利点:
    EnumMapMap の API を持つため、for-each/stream/Collections 等と自然に使える。
  • null キー禁止:
    EnumMapnull キーを受け付けない → キーの欠如を早期に検出できる。

悪い例

序数インデックス/配列で管理するパターン

// 悪い:ordinal() に依存した配列でデータを管理
double[] gravity = new double[Planet.values().length];
gravity[Planet.EARTH.ordinal()] = 9.8;
double g = gravity[Planet.MARS.ordinal()];

問題点:

  • Planet の定義順を変えたり列挙子を挿入するとデータの意味が壊れる。
  • ordinal() を永続化や外部フォーマットで使うと互換性が壊れやすい。
  • 配列だったらキーとして何の enum を使っているか一目でわかりにくい(可読性低下)。

良い例

EnumMap を使うパターン

enum Planet { MERCURY, VENUS, EARTH, MARS }

EnumMap<Planet, Double> gravity = new EnumMap<>(Planet.class);
gravity.put(Planet.EARTH, 9.8);
double g = gravity.get(Planet.MARS); // null チェックが必要(存在保証がないため)

利点:

  • gravity.get(Planet.MARS) と書けば何を参照しているか即座に分かる。
  • enum の宣言順を変えても map のキーはそのままなので壊れない。
  • 内部は配列ベースなので高速。
    ★ただし、get時にnullチェックは忘れずに行う。

初期化・変換の小技

配列や既存データから EnumMap に変換するパターン:

// 全要素を既定値で初期化
EnumMap<Planet, Double> gravity = new EnumMap<>(Planet.class);
for (Planet p : Planet.values()) gravity.put(p, defaultValue);

// 既存の配列から変換する(移行用)
double[] arr = ...;
EnumMap<Planet, Double> map = new EnumMap<>(Planet.class);
Planet[] vals = Planet.values();
for (int i = 0; i < vals.length && i < arr.length; i++) {
    map.put(vals[i], arr[i]);
}

EnumMap と HashMap の比較

  • EnumMap は enum の ordinal() を内部的に使うため 配列アクセス並みに高速でメモリ効率も良い。
  • HashMap<Enum, V> より小さく高速(ハッシュ計算やバケット操作が不要に近い)。
    ※ただし EnumMap はキーが同じ enum 型で固定される場面にのみ使える(汎用性は HashMap の方が高い)。

注意点 / 実務上のポイント

  • null キー不可:
    EnumMap は null をキーにできない(NullPointerException)。null をキーで表す設計は避ける。
  • 値が存在しない場合の扱い:
    get() が null を返す可能性があるので、必ず containsKeygetOrDefaultObjects.requireNonNull 等で扱いを決める。
  • スレッドセーフではない:
    EnumMap 自体はスレッドセーフではない。並行利用なら Collections.synchronizedMap(new EnumMap<>(...))ConcurrentHashMap を検討。
  • 永続化:
    永続化や外部フォーマットで ordinal() を使わない。DB カラムで enum を表現するなら name() や明示的なコードフィールドを使い、fromCode などで復元する。
  • 大きな enum:
    enum の要素数が極端に大きい場合は EnumMap の配列サイズが大きくなるので設計を見直す(通常は問題にならない)。

まとめ

  • Enum をキーにしたデータ管理は 直接 ordinal インデックスで配列管理しないで、EnumMap を使う。
  • EnumMap は読みやすく安全、かつ内部的に効率的(配列ベース)なのでパフォーマンス面でも優れる。
  • 例外:キーが enum 型でない/並列性や特殊要件があるときは別のデータ構造を検討する。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?