- KeySet内部类:
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();
}
}
}
继续上一篇文章的分析
通过源码可以看到,当新建一个KeySet 内部类对象时,并没有调用其他方法,KeySet的父类无参构造函数也没有执行任意代码,
那么KeySet是如何获取key值的?
答案是public final Iterator<K> iterator() { return new KeyIterator(); }
这里使用new KeyIterator()创建了Iterator对象 - KeyIterator内部类:
final class KeyIterator extends HashIterator implements Iterator<K> {
public final K next() { return nextNode().key; }
}
本类继承了HashIterator类,当调用迭代器进行迭代时,会调用HashIterator类的构造函数,HashIterator的属性和构造函数如下
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() { // 构造函数
expectedModCount = modCount; // 获取迭代时Map的修改次数,用于对比迭代时判断Map是否被其他线程修改
Node<K,V>[] t = table; // 可以看到,迭代器本质上还是对table进行操作
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null); // 查找table第一个不为null的节点
}
}
至此,已经清楚迭代器的操作,其实就是操作底层的table来实现的,也就是直接操作键值对,正因为如此,迭代器的底层直接根据table进行操作,所以如果有别的容器持有了这个迭代器内部类,就可以直接实现同步中的可见性 : 对HashMap的改变体现在table,而传递出去的内部类可以访问table
回到KeyIterator的类,内部的next()方法:
方法调用的是HashIterator父类的nextNode(),然后直接返回Node<K,V>的key属性,以获取key值
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next; // 构造函数查找到的,table第一个不为null的节点
if (modCount != expectedModCount) // 如果其他线程修改了Map,就抛出异常
throw new ConcurrentModificationException();
if (e == null) // 节点为null,表示没有找到节点,并抛出异常
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) { // 如果节点的链表为null,但是table不为null时,继续遍历,查找下一个不为null的节点
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
所以KeySet可以使用KeySet.iterator().next()进行遍历,
也可以使用foreach进行遍历,foreach内部会触发KeySet.iterator().next()
HashMap源码 7 - KeySet分析
最新推荐文章于 2021-08-19 16:59:04 发布