JDK 8 HashMap反復性能比較


概要
Mapはキーによるデータへのアクセスが容易で、Javaを含む多くの言語でよく使われるデータ構造です
JavaではO(1)アクセス可能なMapインプリメンテーションHashMapがよく用いられる.
たまに保存した値を全部巡回する必要がある場合もありますか?
(最初はこのようなことは起こらないほうがいいですが…)
仕事をしているうちに、HashMapの反復が必要になることがありました.
リファレンス
参考記事

上記参考文書を受け取った5つの分封の回答では,keySet for,keySet foreach,entrySet for,entrySet foreach,foreachの全値サイクルに対するbanchmarkテスト結果を示した.
その結果,jdk 8はentrysetのforeach ramda処理が最も速いと主張した.
n/a.環境
使用する環境は次のとおりです.


コード#コード#
テスト用に用意したサンプルコードは次のとおりです.
import java.util.Map;
import java.util.HashMap;

public class MapIterComp {
    
    private static int size = 10000, round = 5;
    private static long sum, start, end;

    private static void print(String name, long value) {

        System.out.print(String.format("%-20s: ", name));
        System.out.print(String.format("%5d", (end - start)));
        System.out.println(" ms");
    }

    private static void compare() {

        final Map<Integer, Integer> map = new HashMap<>();

        for (int i = 0; i < size; i++) map.put(i, i);

        System.out.println("[size]: " + size);
        System.out.println("[round]: " + round);
        
        // 1. key set for loop
        sum = 0;
        for (int i = 0; i < round; i++) {

            start = System.currentTimeMillis();
            for (Integer key: map.keySet()) sum += map.get(key);
            end = System.currentTimeMillis();
            sum += (end - start);
        }
        print("keySet() for", sum/round);

        // 2. key set for each
        sum = 0;
        for (int i = 0; i < round; i++) {

            start = System.currentTimeMillis();
            map.keySet().forEach(k -> sum += map.get(k));
            end = System.currentTimeMillis();
            sum += (end - start);
        }
        print("keySet() forEach", sum/round);

        // 3. entry set for loop
        sum = 0;
        for (int i = 0; i < round; i++) {

            start = System.currentTimeMillis();
            for (Map.Entry<Integer, Integer> e: map.entrySet()) sum += e.getValue();
            end = System.currentTimeMillis();
            sum += (end - start);
        }
        print("entrySet() for", sum/round);

        // 4. entry set for each
        sum = 0;
        for (int i = 0; i < round; i++) {

            start = System.currentTimeMillis();
            map.entrySet().forEach(e -> sum += e.getValue());
            end = System.currentTimeMillis();
            sum += (end - start);
        }
        print("entrySet() forEach", sum/round);

        // 5. values for loop
        sum = 0;
        for (int i = 0; i < round; i++) {

            start = System.currentTimeMillis();
            for (Integer value: map.values()) sum += value;
            end = System.currentTimeMillis();
            sum += (end - start);
        }
        print("values() for", sum/round);
        
        // 6. values for each
        sum = 0;
        for (int i = 0; i < round; i++) {

            start = System.currentTimeMillis();
            map.values().forEach(v -> sum += v);
            end = System.currentTimeMillis();
            sum += (end - start);
        }
        print("values() for each", sum/round);
        
        // 7. for each
        sum = 0;
        for (int i = 0; i < round; i++) {

            start = System.currentTimeMillis();
            map.forEach((k, v) -> sum += v);
            end = System.currentTimeMillis();
            sum += (end - start);
        }
        print("for each", sum/round);
    }

    public static void main(String[] args) {
        
        if (args.length == 2) {

            size = Integer.valueOf(args[0]);
            round = Integer.valueOf(args[1]);
        }

        if (args.length == 1) size = Integer.valueOf(args[0]);

        compare();
    }
}
テスト結果
1万環、10回ごとに平均

10万ループ、平均10回ごと

50万ループ、平均10回ごと

100万ループ、平均10回ごと

500万ループ、平均10回ごと

1000万ループ、平均10回ごと

5000万ループ、平均10回ごと

テストの結論
entrySet().forEachまたはvalues()です.いい感じ
JDK 8に追加されたforeach方式は、従来のforサイクル処理と比較して、より高い性能上の利点を有する.
特に驚くべきことに、身長と価値を一度に持ち歩くので、もっと重くなると思っていたEntrySetは思ったよりもほぼ早く処理していた.
このテストコードには整数を加算する単純な動作しか含まれていないため、時間差は小さくなりますが、より複雑な論理を実行すると仮定すると、ループコードごとにパフォーマンスの差が大きくなります.