Java遍历Map效率对比
Java 中Map容器的遍历有多种方式,但是不同的方式效率会大有不同,以前没有注意这些细节,随意使用遍历方式在本地可能没有什么影响,但是在项目在高频使用需要特别注意,尽量使用高效的方式。
首先,Map.Entry<K,V>是可以包含了Key和Value的,keySet包含了所有的Key,再使用get方法可以拿到对应的Value;所以包含Key和Value内容的就有两种方式,我们再看访问模式,可以使用迭代器Iterator 访问,也可以使用For循环访问;那么2种包含方式和访问模式组合起来就是4种访问方式
初始化Map
导入相应的包
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
我们选择不同规模Key记录来测试不同方式的差异
Map<String, String> map = new HashMap<>();
String key, value;
int num = 10000; // num值从{1w, 10w, 100w, 1000w}中选择
for (int i = 1; i <= num; i++) {
key = "mykey" + i;
value = "myvalue" + i;
map.put(key, value);
}
keySet 与 Iterator
long startTime1 =System.currentTimeMillis();
Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()){
key=iter.next();
value=map.get(key);
}
long endTime1 =System.currentTimeMillis();
System.out.println("Run:"+(endTime1-startTime1)+"ms KeySet && Iterator");
keySet 与 for-loop
long startTime2 =System.currentTimeMillis();
for(String key2:map.keySet()){
value=map.get(key2);
}
long endTime2 =System.currentTimeMillis();
System.out.println("Run:"+(endTime2-startTime2)+"ms KeySet && For-Loop");
Map.Entry<K,V> 与 Iterator
long startTime3=System.currentTimeMillis();
Iterator<Map.Entry<String,String>> iter3 =map.entrySet().iterator();
Map.Entry<String,String> entry3;
while (iter3.hasNext()){
entry3 = iter3.next();
key = entry3.getKey();
value=entry3.getValue();
}
long endTime3 =System.currentTimeMillis();
System.out.println("Run:" +(endTime3-startTime3)+"ms Map.Entry && Iterator");
Map.Entry<K,V> 与 for-loop
long startTime4=System.currentTimeMillis();
for(Map.Entry<String,String> entry4:map.entrySet()){
key=entry4.getKey();
value=entry4.getValue();
}
long endTime4 =System.currentTimeMillis();
System.out.println("Run:"+(endTime4-startTime4) +"ms Map.Entry && For-Loop");
效率比较
以下运行时间单位为毫秒(ms)
Key数量 | Entry && Iterator | Entry && For-Loop | KeySet && Iterator | KeySet && For-Loop |
---|---|---|---|---|
10000(1w) | 2 | 1 | 3 | 2 |
100000(10w) | 8 | 6 | 10 | 8 |
1000000(100w) | 38 | 32 | 39 | 36 |
10000000(1000w) | 236 | 251 | 316 | 319 |
对比结论
多次验证,上述结果变化基本不大。使用Map.Entry<K,V>与for循环的组合是最有效率的
Map常用方法
Set<K> keySet()
Collection<V> values()
V get(Object key)
boolean containsKey(Object key)
boolean containsValue(Object value)
V getOrDefault(Object key, V defaultValue)
Java8 Map的遍历使用了新的方式forEach,这个函数就非常方便了,但是forEach中的值k,v是不可变的,在Java中是final的,这样就无法将k,v赋值给外部的变量了。如果一定要修改外部的值,需要用一个初值来累加/减/乘/除等操作
Map<String, String> map = new HashMap<>();
String key, value;
int num = 10;
for (int i = 1; i <= num; i++) {
key = "mykey" + i;
value = "myvalue" + i;
map.put(key, value);
}
map.forEach((k, v) -> { // 非常方便
System.out.println(k + ":" + v);
});
把一个Map全部放到另外一个Map中
void putAll(Map<? extends K,? extends V> m)
删除一个键值对
boolean remove(Object key)
替换一个键值对
boolean replace(K key, V oldValue, V newValue)
替换Map中所有Entry的value值,这个值由旧的key和value计算得出
void replaceAll(BiFunction<? super K,? super V,? extends V> function)