今日进行CR的时分,发现代码中对HashMap进行遍历时,运用的是for(key in map.keys)
,就想起了之前看到的一个文章,说运用entrySet()
进行遍历功能更高。其时没有追查,可是最近看《代码整齐之道》,学到了
勒布朗(LeBlanc)法则:稍后等于永不(Later equals never)
那么今日一定要把其间的原理搞清楚。
用法
- keySet()
for (key in map.keys) {
map[key]
}
- entrySet()
for (entry in map.entries) {
entry.value
}
差异
-
keySet()回来的是一个关于K的Set (第一次查询),要获取value,还需要经过map[key]获取 (第二步查询)。
-
entrySet()回来的是一个K,V键值对的Set (第一次查询),获取value直接调用entry.value (不需要查询)。
考虑
- 尽管运用
entrySet()
进行遍历,功能更好,可是多线程下就没办法确保安全性了,所以在多线程情况下,建议考虑运用ConcurrentHashMap()
。 - 观察
entrySet()
办法,发现entrySet
为null
时,回来的是EntrySet()
目标,可是看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(); }
// 这里回来了EntryIterator()
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry<?, ?> e))
return false;
Object key = e.getKey();
Node<K,V> candidate = getNode(key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry<?, ?> e) {
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 (Node<K,V> e : tab) {
for (; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
能够发现iterator()
办法回来了EntryIterator()
目标,深入其间,发现其继承了HashIterator
,而HashIterator
结构办法则是从关联到了table
,在HashMap
的put办法中,就是向table
中插入值。
或许有人会问,这与EntrySet()
有什么关系呢?for
循环怎么获取到的Set
目标呢?
经过对kotlin
代码转成Bytecode
,再反编译成java
言语,能够发现:
var3 = map.entrySet().iterator();
while(var3.hasNext()) {
Map.Entry entry = (Map.Entry)var3.next();
entry.getValue();
}
就是说for (entry in map.entries)
,实际上是遍历的迭代器,这就与前面EntrySet
的iterator()
办法的回来值关联起来了,忽然想起了名侦察柯南的画面,哈哈哈