JDK8之前,可以使用ketSet或者entrySet来遍历HashMap,JDK8中引入了map.forEach来进行遍历
1. keySet和entrySet
1.1 基本用法
keySet:
Map<String,Object> map = new HashMap<>();
Iterator it = map.keySet().iterator();
Object key;
Object value;
while(it.hasNext()){
key = it.next();
value = map.get(key);//性能差异
System.out.println(key+":"+value);
}
entrySet:
Map<String,Object> map = new HashMap<>();
Iterator it = map.entrySet().iterator();
Object key;
Object value;
while(it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
key = entry.getKey();
value = entry.getValue();//性能差异
System.out.println(key+":"+value);
}
1.2 源码分析
keySet:
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
entrySet
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
从源码中可以看出,当要得到某个value时,keySet需要从hashMap中get,entrySet相比keySet少了遍历table的过程,这就是两者性能上的主要差别
map.forEach()
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
源码分析可知,map.foreach()本质上还是使用的entrySet
实例
package com.feiyu.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @author maokeluo
* @desc 遍历hashMap性能比较
* @create 17-9-2
*/
public class TraverseHsahMap {
public static void main(String[] args) throws InterruptedException {
Map<String, String> map = new HashMap<String, String>();
int num = 10000000;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < num; i++){
String temp = String.valueOf(i);
map.put(temp,temp);
}
}
});
thread.start();
thread.join();
//forEach map.entrySet
long start1 = System.currentTimeMillis();
for (Map.Entry<String, String> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
long end1 = System.currentTimeMillis() - start1;
System.out.println("forEach map.entrySet遍历HashMap使用时间:"+end1);
//显式调用map.entrySet()的集合迭代器
long start2 = System.currentTimeMillis();
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
entry.getKey();
entry.getValue();
}
long end2 = System.currentTimeMillis() - start2;
System.out.println("显式调用map.entrySet()的集合迭代器遍历HashMap使用时间:"+end2);
//forEach map.keySet在调用get取值
long start3 = System.currentTimeMillis();
for (String key : map.keySet()) {
map.get(key);
}
long end3 = System.currentTimeMillis() - start3;
System.out.println("forEach map.keySet在调用get取值遍历HashMap使用时间:"+end3);
//forEach map.entrySet(),用临时变量保存map.entrySet()
long start4 = System.currentTimeMillis();
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
entry.getKey();
entry.getValue();
}
long end4 = System.currentTimeMillis() - start4;
System.out.println("forEach map.entrySet(),用临时变量保存map.entrySet()使用时间:"+end4);
}
}
结果
| map size | 10000 | 100000 | 1000000 | 10000000 |
|——–|——–|
| forEach entrySet | 2ms | 11ms | 32ms | 199ms |
| for iterator entrySet| 1ms | 5ms | 29ms | 203ms |
| forEach keySet | 2ms | 7ms | 54ms | 273ms |
| for entrySet=entrySet()| 1ms | 5ms | 28ms | 189ms |
遍历方式结论总结
- hashMap的循环遍历,如果不仅需要key又需要value,使用
for (Map.Entry<String, String> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
效率更高
- 如果只是遍历key不需要value,可以直接使用
for (String key : map.keySet()) {
//操作key
}