在api文档中这三个方法返回的都是视图,今天从源码层面上分析,为什么是视图。
keySet():
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
values():
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
private final class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
HashMap.this.clear();
}
}
entrySet():
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());
}
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> e = (Map.Entry<K,V>) o;
Entry<K,V> candidate = getEntry(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
HashMap.this.clear();
}
}
从iterator()以外的其它方法已经可以看出一些端倪,它们内部都是调用的HashMap的方法进行相关的操作。
现在主要关注iterator()方法。该方法会在遍历集合时调用,如果明白了它,就可以知道遍历时,具体在遍历什么。
下面是三个iterator()方法的具体实现:
Iterator<K> newKeyIterator() {
return new KeyIterator();
}
Iterator<V> newValueIterator() {
return new ValueIterator();
}
Iterator<Map.Entry<K,V>> newEntryIterator() {
return new EntryIterator();
}
keyIterator、ValueIterator、EntryIterator定义如下:
private final class ValueIterator extends HashIterator<V> {
public V next() {
return nextEntry().value;
}
}
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
到这里我们可以看到他们都继承自同一个HashIterator<E>
,下面是HashIterator<E>
源码:
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry<K,V> current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
HashIterator<E>
实现了Iterator<E>
接口但并没有实现next方法,所以HashIterator<E>
被声明为抽象类,next方法在keyIterator、ValueIterator、EntryIterator中都进行了具体的实现,其实现都基于HashIterator<E>
的nextEntry()方法。下面来详细分析下nextEntry()方法。
nextEntry()顾名思义,应该是返回下一个Entry对象,前面说到HashMap的键值对都存储在数组+链表结构的Entry对象中,具体可以参考另一篇博客从源码解读HashMap数据结构,根绝keySet(),values(),entrySet()返回视图的结论来看,这里的nextEntry()访问的仍然应该是HashMap的数据,也就是说三个返回的集合都是没有实体的,仍然是换了一种形式访问HashMap的数据而已,所进行的操作也直接反映在HashMap上,下面来具体看nextEntry()方法以验证这种推测:
首先看HashIterator的构造方法:
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
首先会拿到HashMap的结构基础Entry[] table,while循环就是找到第一个有数据的链表,在没有数据的情况下t[index++]==null
再看 nextEntry():
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
核心意思是如果当一个链表通过e.next遍历完以后,会再次通过while循环,寻找下一个有数据的链表。
看得出来,nextEntry拿到的确实仍是HashMap的数据,然后在keyIterator、ValueIterator、EntryIterator的next方法中分别拿nextEntry().getKey()、nextEntry().value、nextEntry()。
所以说keySet(),values(),entrySet()返回的集合是没有额外的数据实体的,都依赖于HashMap的数组+链表结构的Entry数据。所以称返回的是视图。