Java で Map を回す時は entrySet の方が早い(とりあえず HashMap の話)


最近,SQL ばかり書いてて久しぶりに Java 書いたら
「Map ってどうやって回すんだっけ??」
という超初歩的な疑問がwwww


拡張 for 文で keySet 回せばいいかなぁと思ったら
id:sett-4
「entrySet まわした方が早かった筈ですよ」
って言われた.


勝手な想像で,entrySet って Iterable#iterator() の Iterator#next() で

  return new Map.Entry(key,map.get(key));

的な事してて逆に遅いんじゃね??って思ったので
調べてみた.

とりあえずソース読んでみる


そしたら

    public Set<Map.Entry<K,V>> entrySet() {
	return entrySet0();
    }

    private Set<Map.Entry<K,V>> entrySet0() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }

な実装でした


逆に keySet の実装見たら

    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }

ってなってるんだけど
最終的には

    private final class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }

って Entry から key だけ取り出してたwwww

実験もしてみる


こんなコードで実験してみました.

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.time.StopWatch;


public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		int COUNT = 10000;
		Map<Integer,String> map = new HashMap<Integer, String>();
		for(int i = 0 ; i < 10000000 ; i++){
			map.put(i, "hoge");
		}
		StopWatch stopWatch = new StopWatch();
		
		List<Long> timeEntry = new ArrayList<Long>(COUNT);
		List<Long> timeKey = new ArrayList<Long>(COUNT);
		for(int i = 0 ; i < COUNT ; i++){
			stopWatch.reset();
			stopWatch.start();
			for(Entry<Integer, String> entry : map.entrySet()){
				entry.getKey();
				entry.getValue();
			}
			stopWatch.stop();
			timeEntry.add(stopWatch.getTime());
		}
		for(int i = 0 ; i < COUNT ; i++){
			stopWatch.reset();
			stopWatch.start();
			for(Integer key: map.keySet()){
				map.get(key);
			}
			stopWatch.stop();
			timeKey.add(stopWatch.getTime());
		}
		Collections.sort(timeEntry);
		Collections.sort(timeKey);
		System.out.println("entrySet 最小:" + timeEntry.get(0) + " | 最大:" + timeEntry.get(timeEntry.size() - 1) + " | 平均:" + getAverage(timeEntry));
		System.out.println("keySet 最小:" + timeKey.get(0) + " | 最大:" + timeKey.get(timeEntry.size() - 1) + " | 平均:" + getAverage(timeKey));
	}
	static Long getAverage(List<Long> list){
		Long all = 0L;
		for(Long i : list){
			all += i;
		}
		return all/list.size();
	}
}


10000000 個の値を持っている HashMap を 10000 回回して
かかった時間の最大と最小と平均を出してみました.

で,結果は……

entrySet 最小:111 | 最大:411 | 平均:117
keySet 最小:172 | 最大:485 | 平均:179


と言う事で……

結論


HashMap を回す時は entrySet の方が早い!!!