HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

列挙型専用のSet - java.util.EnumSet

EnumMapと同じようなことを、EnumSetに対してもやってみた。
列挙型はEnumMapの場合と同じだが、再掲。

enum A {
    A_1,
    A_2,
    A_3;
}

次にテスト用のクラス。ただ、EnumSetクラスにはpublicなコンストラクタがなく、代わりに要素なしのEnumSetオブジェクトを生成するEnumSet#noneOf(Class)メソッドを使用する。

import java.util.EnumSet;
import java.util.List;
import java.util.Set;

public class EnumSetTester {

    public void test(int n, List<Set<A>> list) {
        Set<A> s1 = EnumSet.noneOf(A.class);
        for (A a : A.values()) {
            s1.add(a);
        }
        for (int i = 0; i < n; ++i) {
            Set<A> s2 = EnumSet.noneOf(A.class);
            s2.addAll(s1);
            list.add(s2);
        }
    }
}

同様に、比較対象のHashSetに対するテスト用のクラス。

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class HashSetTester {

    public void test(int n, List<Set<A>> list) {
        Set<A> s1 = new HashSet<A>();
        for (A a : A.values()) {
            s1.add(a);
        }
        for (int i = 0; i < n; ++i) {
            Set<A> s2 = new HashSet<A>();
            s2.addAll(s1);
            list.add(s2);
        }
    }

これらの処理の呼び出し側。

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class Main {

    public static void main(String[] args) {
        int n = 100000;

        List<Set<A>> list = new ArrayList<Set<A>>(n);

        dumpMemoryUsage("before");
        new HashSetTester().test(n, list);
//        new EnumSetTester().test(n, list);
        dumpMemoryUsage("after");
    }

    private static void dumpMemoryUsage(String msg) {
        NumberFormat nf = new DecimalFormat("0.000");
        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));
    }
}

で、実行結果。
HashSetの場合。

before : max       total     free      used   
       :  63.562MB   1.938MB   1.273MB   0.665MB
after  : max       total     free      used   
       :  63.562MB  28.406MB   7.791MB  20.616MB

EnumSetの場合。

before : max       total     free      used   
       :  63.562MB   1.938MB   1.273MB   0.665MB
after  : max       total     free      used   
       :  63.562MB   7.785MB   2.628MB   5.157MB

やはり同様に差が出る。
JavaDocやソースを見てみると、こちらはビットパターンによって、列挙型のどの要素がSetに含まれているかを保持している。要素数が64個以下の場合はlong値1つだけを持つEnumSet実装(RegularEnumSet)を使用し、65個以上ある場合はlong値の配列を持つEnumSet実装(JumboEnumSet)を使用している。