LinkedHashMap
如何保障有序的遍历
前一篇《JDK容器学习之LinkedHashMap (一):底层存储结构分析》 中介绍了LinkedHashMap继承自
HashMap
,且内部维护一个双向链表,那么其遍历方式是否就是对这个双向链表的遍历呢?
1. 根据Map.Entry
进行遍历
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ?
(entrySet = new LinkedEntrySet()) : es;
}
final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size()
{ return size; }
public final void clear()
{ LinkedHashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new LinkedEntryIterator();
}
public final boolean contains(Object o) {
// ...
}
public final boolean remove(Object o) {
// ...
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
// ...
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
// ...
}
}
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
上面给出关键的链路,entrySet
方法调用,首次会创建一个LinkedEntrySet
, 内部实现迭代器 LinkedEntryIterator
所以迭代的主要逻辑就是LinkedEntryIterator
的实现方式了
abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount;
LinkedHashIterator() {
// 保证从链表的头开始扫描
next = head;
expectedModCount = modCount;
current = null;
}
public final boolean hasNext() {
return next != null;
}
final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
current = e;
next = e.after;
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
迭代器的实现也比较清楚,首先是在构造器中,将next
指向双向链表的头head
nextNode
方法中,返回next
节点,并将next指向链表的下一个节点
2. 根据keys进行遍历
public Set<K> keySet() {
Set<K> ks;
return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
}
final class LinkedKeySet extends AbstractSet<K> {
public final int size()
{ return size; }
public final void clear()
{ LinkedHashMap.this.clear(); }
public final Iterator<K> iterator() {
return new LinkedKeyIterator();
}
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() {
// xxx
}
public final void forEach(Consumer<? super K> action) {
// xxx
}
}
final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator<K> {
public final K next() { return nextNode().getKey(); }
}
从上面的调用链来看,迭代逻辑和上面一样,唯一的区别是根据key进行迭代时,迭代器的next()
方法直接返回Node节点的key,而之前是返回整个Node节点
3. 遍历values
基本逻辑同上,省略
小结
从遍历的逻辑来看,LinkedHashMap
的遍历实际上就是遍历内部维护的双向链表
相关博文
- JDK容器学习之HashMap (一) : 底层存储结构分析
- JDK容器学习之HashMap (二) : 读写逻辑详解
- JDK容器学习之HashMap (三) : 迭代器实现
- JDK容器学习之TreeMap (一) : 底层数据结构
- JDK容器学习之LinkedHashMap(一):底层存储结构分析
网友评论