int / Integer 変換コストについて (Java)

暗黙的なボクシング処理

Javaの int と Integer は殆ど同じような感覚で使ってると思いますが、
int から、Integer にする時は、boxing
Integer から、int になる時は、unboxing という処理が暗黙的に行われています。

微々たるものですが、やはり処理コストがかかっているので、知識として知っておくと良いかと思います。

処理速度比較

例えば、Integerを使ったジェネリクスを扱う時に、ループ処理などでは、感覚的にintで扱う事はよくありますが。

1千万の要素をもつハッシュマップから、
boxingが発生しない場合と、int -> Integerでboxing処理が発生する場合で、
どのくらい速度差があるか測定してみました。

テスト用のマップ作成

// 10,000,000要素のマップを作成
Map<Integer, Integer> map = IntStream.rangeClosed(1, 10000000)
        .boxed()
        .collect(Collectors.toMap(i -> i, i -> i));

Integerを使った場合

Integer[] keys = map.keySet().toArray(new Integer[0]);

for (int i = 0; i < 10; i++) {
    long processStart = System.nanoTime(); // 測定開始

    long sum = 0;

    for (Integer _integer : keys) {
        sum += map.get(_integer);
    }

    long processEnd = System.nanoTime(); // 測定終了
    System.out.println(String.format("PROCESS_TIME: %d[ms]", (processEnd - processStart) / 1000000));
}
PROCESS_TIME: 98[ms]
PROCESS_TIME: 77[ms]
PROCESS_TIME: 72[ms]
PROCESS_TIME: 111[ms]
PROCESS_TIME: 71[ms]
PROCESS_TIME: 65[ms]
PROCESS_TIME: 97[ms]
PROCESS_TIME: 66[ms]
PROCESS_TIME: 66[ms]
PROCESS_TIME: 96[ms]

intを使った場合

int[] keys = map.keySet().stream().mapToInt(i -> i).toArray();

for (int i = 0; i < 10; i++) {
    long processStart = System.nanoTime(); // 測定開始

    long sum = 0;

    for (int _int : keys) {
        sum += map.get(_int); // int -> Integerへのboxing処理が入っている
    }

    long processEnd = System.nanoTime(); // 測定終了
    System.out.println(String.format("PROCESS_TIME: %d[ms]", (processEnd - processStart) / 1000000));
}
PROCESS_TIME: 265[ms]
PROCESS_TIME: 194[ms]
PROCESS_TIME: 210[ms]
PROCESS_TIME: 198[ms]
PROCESS_TIME: 211[ms]
PROCESS_TIME: 188[ms]
PROCESS_TIME: 174[ms]
PROCESS_TIME: 155[ms]
PROCESS_TIME: 205[ms]
PROCESS_TIME: 202[ms]

Map::get が求めるのは参照型なので、
int を渡した場合、暗黙的にboxing処理が発生しています。
V get(Object key);

Integer と int どちらを使うほうが良いのか?

速度差は2倍くらい違うみたいですが、
正直、1千万回のループでもこのくらいの差なので〜、・・・どちらでもいい気がします。

ちなみに、速度差測定した時に使用したJavaバージョンは、8と10で特に大差ありませんでした。

コメントを残す