我们在写代码的时候经常会用到HashMap这个方法的entrySet()方法,这个方法返回返回的是一个Set对象,很多人以为返回的是一个包含了Map里面所有键值对的一个集合对象,这个理解不准确,怎么说呢,通过这个Set对象,我们确实可以获取到Map里面存放的所有键值对,但是这个集合对象本身是不存放数据的,它只是助于我们遍历Map中的数据,类似于Iterator。下面我们来看看源码简要分析一下。
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
上面是entrySet()的源码,我么可以知道返回的其实是HashMap内部维护的一个引用entrySet,这个应用是EntrySet类型的。我们本能在HashMap的源码中找一下entrySet是何时填充数据的,搜遍代码我们会发现没有该逻辑,这个很奇怪啊,我们来看一下EntrySet的类定义,该类定义在HashMap的内部:
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;
// Android-changed: Detect changes to modCount early.
for (int i = 0; (i < tab.length && modCount == mc); ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
我们重点看一下EntrySet的forEach方法,可以看出这个方法可以读取到其所在的HashMap对象的存储的所有键值对,而我们经常使用entrySet方法就是用来进行foreach循环的。
好了,HashMap.entrySet( )本身并没有难度,但是有些朋友可能会对这个api理解并不准确,记住,该方法并不是返回一个存储数据的集合,它只是一个视图窗口。HashMap.ketSet()原理类似。