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 の方が早い!!!