最近面试中被问到 keySet() 的原理,总结一下,value()方法也与此类似。
简单来说,HashMap和ConcurrentHashMap中的 Set keySet() 都是实现了Iterator接口或者接口的思想。keySet()方法返回一个内部引用,并指向一个内部类对象,该内部类重写了迭代器方法,当在增强for循环时才调用,并从外部类的table中取值。HashMap中的 内部类HashIterator类实现了Iterator接口,先来看看HashMap的源码,
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;
}
}
//迭代器的操作,都是根据底层的table来实现的,也就是直接操作键值对Entry<K,V> 。在得到Entry之后再获得它的key或者value。正因为如此,迭代器的底层直接根据table进行操作,所以如果有别的容器持有了这个迭代器内部类,就可以直接实现同步中的可见性:对HashMap的改变体现在table,而传递出去的内部类可以访问table。
ConcurrentHashMap中的keyset()源码
abstract class HashIterator {
int nextSegmentIndex;
int nextTableIndex;
HashEntry<K,V>[] currentTable;
HashEntry<K, V> nextEntry;
HashEntry<K, V> lastReturned;
HashIterator() {
nextSegmentIndex = segments.length - 1;
nextTableIndex = -1;
advance();
}
public boolean hasMoreElements() { return hasNext(); }
final void advance() {
if (nextEntry != null && (nextEntry = nextEntry.next) != null)
return;
while (nextTableIndex >= 0) {
if ( (nextEntry = currentTable[nextTableIndex--]) != null)
return;
}
while (nextSegmentIndex >= 0) {
Segment<K,V> seg = segments[nextSegmentIndex--];
if (seg.count != 0) {
currentTable = seg.table;
for (int j = currentTable.length - 1; j >= 0; --j) {
if ( (nextEntry = currentTable[j]) != null) {
nextTableIndex = j - 1;
return;
}
}
}
}
}
public boolean hasNext() { return nextEntry != null; }
HashEntry<K,V> nextEntry() {
if (nextEntry == null)
throw new NoSuchElementException();
lastReturned = nextEntry;
advance();
return lastReturned;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
ConcurrentHashMap.this.remove(lastReturned.key);
lastReturned = null;
}
}
可以看出来直接操作的Segment和里面的table,看advance()方法,对整个对象进行遍历。
总结一下:HashMap和ConcurrentHashMap中的 Set keySet()方法返回一个内部类对象的引用,该内部类重写了迭代器的方法,当在增强for循环时才调用,并从外部类的table中取值。