列挙型専用のMap - java.util.EnumMap
Java 5から列挙型(enum)が導入されたのに合わせて、列挙型の値をキーとするEnumMapというクラスが提供されているのを知り、またメモリ効率がよいという記述をどこかで見たので、ちょっと実験。
まず列挙型。
enum A {
A_1,
A_2,
A_3;
}
次にテスト用のクラス。EnumMapを一つ作り、そのコピーを指定した個数だけ生成してリストに格納する。
import java.util.EnumMap; import java.util.List; import java.util.Map; public class EnumMapTester { public void test(int n, List<Map<A, String>> list) { Map<A, String> m1 = new EnumMap<A, String>(A.class); for (A a : A.values()) { m1.put(a, a.toString()); } for (int i = 0; i < n; ++i) { list.add(new EnumMap<A, String>(m1)); } } }
もう一つテスト用。比較対象はEnumMapのJavaDocに挙がっているHashMap。処理内容はEnumMapの場合と同じ。
import java.util.HashMap; import java.util.List; import java.util.Map; public class HashMapTester { public void test(int n, List<Map<A, String>> list) { Map<A, String> m1 = new HashMap<A, String>(1); for (A a : A.values()) { m1.put(a, a.toString()); } for (int i = 0; i < n; ++i) { list.add(new HashMap<A, String>(m1)); } } }
これらの処理の呼び出し側。処理の呼び出しの前後でメモリ使用量を標準出力に出力する。
import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { int n = 100000; List<Map<A, String>> list = new ArrayList<Map<A, String>>(n); dumpMemoryUsage("before"); new HashMapTester().test(n, list); // new EnumMapTester().test(n, list); dumpMemoryUsage("after"); } private static void dumpMemoryUsage(String msg) { NumberFormat nf = new DecimalFormat("#.###"); Runtime rt = Runtime.getRuntime(); long max = rt.maxMemory(); long free = rt.freeMemory(); long total = rt.totalMemory(); System.out.printf("%-7s: %-7s %-7s %-7s %-7s\n", msg, "max", "total", "free", "used"); System.out.printf("%-7s: %7sMB %7sMB %7sMB %7sMB\n", "", nf.format(max / 1024.0 / 1024), nf.format(total / 1024.0 / 1024), nf.format(free / 1024.0 / 1024), nf.format((total - free) / 1024.0 / 1024)); } }
これをHashMap、EnumMapのそれぞれの場合で実行してみると、出力されるメモリ使用量は以下のような感じ。
HashMapの場合。
before : max total free used : 63.562MB 1.938MB 1.273MB 0.665MB after : max total free used : 63.562MB 28.004MB 9.100MB 18.904MB
EnumMapの場合。
before : max total free used : 63.562MB 1.938MB 1.273MB 0.665MB after : max total free used : 63.562MB 7.777MB 1.092MB 6.685MB
まぁ、100000個もMapを作ることはめったにないだろうけど、明らかな差があるのは確かなようだ。
念のため、EnumMapのソースコードを見てみると、なるほど、値は配列に格納し、java.lang.Enumのordinal()メソッドが0(zero)から(要素数-1)の値を返すことを利用して、それをそのまま配列の添え字に使用している。