LinkedHashMap源码解析

Collection系列

  1. HashMap --> HashMap源码解析_yolan6824的博客-CSDN博客
  2. LinkedHashMap --> 即本篇

这篇文章只志在搞懂一个问题,LinkedHashMap是怎么实现顺序遍历HashMap的呢?

先来看下LinkedHashMap的基本结构。

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{/****/}

LinkedHashMap,继承了HashMap,说明结构与HashMap一致。

从上一篇文章可以知道HashMap之所以不能顺序遍历,是因为它的插入顺序就是无序的。但是LinkedHashMap却实现了顺序遍历,它是怎么实现的呢?

想要搞清楚LinkedHashMap是怎么实现顺序遍历的,直接先看下它是如何实现迭代器的。

图中LinkedHashMap自己实现了几个迭代器。LinkedEntryIterator,LinkedHashIterator,LinkedKeyIterator,LinkedValueIterator。

其中LinkedHashIterator是基类,LinkedEntryIterator(遍历Map.Entry,即每一个key,value)和LinkedKeyIterator(遍历Key),LinkedValueIterator(遍历value)都继承了LinkedHashIterator。

先来看下基类。

// LinkedHashMap.java
abstract class LinkedHashIterator {
    LinkedHashMapEntry<K,V> next;
    LinkedHashMapEntry<K,V> current;
    int expectedModCount;

    LinkedHashIterator() {// 初始化next
        next = head;
        expectedModCount = modCount;
        current = null;
    }

    public final boolean hasNext() {// 遍历的结束条件
        return next != null;
    }

    // nextNode方法没什么特别的,就是简单的遍历链表而已
    // 唯一需要注意的是,这里nextNode返回的是LinkedHashMapEntry,
    // 而不是HashMap的Node
    final LinkedHashMapEntry<K,V> nextNode() {// 遍历链表
        LinkedHashMapEntry<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;
    }
}
// 通用调用了nextNode的方法
final class LinkedKeyIterator extends LinkedHashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().getKey(); }
}

final class LinkedValueIterator extends LinkedHashIterator
    implements Iterator<V> {
    public final V next() { return nextNode().value; }
}

final class LinkedEntryIterator extends LinkedHashIterator
    implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}

从LinkedKeyIterator和LinkedValueIterator和LinkedEntryIterator迭代器的实现可以看出,实现遍历都是调用的基类的nextNode方法。

而基类的nextNode方法,只是做了简单的链表遍历。而且是一个完整的链表,跟HashMap中为了解决哈希冲突而构建的链表不同。初次之外,返回的也是LinkedHashMapEntry,而不是HashMap中的Node。

所以,可以知道秘密实际就在LinkedHashMapEntry当中。

下面看下LinkedHashMapEntry做了什么?

static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
    LinkedHashMapEntry<K,V> before, after;
    LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

LinkedHashMapEntry也特别简单,就是存储了一个before,after的指针。

从这里其实可以猜测,LinkedHashMap实际上应该是在put的过程中,顺便构建了一个双链表。

这样,在插入,查询的时候,可以使用HashMap的快速,而在遍历的时候,就根据这个链表进行遍历。简直完美!

->_->唯一的不足就是耗费了空间而已。但是哪有这么多两全其美的事情呢~

// LinkedHashMap.java
// head的赋值,newNode在HashMap的putVal方法也会调用
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    LinkedHashMapEntry<K,V> p =
        new LinkedHashMapEntry<K,V>(hash, key, value, e);
    linkNodeLast(p);// 在创建一个新建节点的同时,构建p和链表的最后一个节点的关系:p.before = last;last.after = p;构建双链表
    return p;
}

// 构建双链表
private void linkNodeLast(LinkedHashMapEntry<K,V> p) {
    LinkedHashMapEntry<K,V> last = tail;
    tail = p;
    if (last == null)
        head = p;
    else {
        p.before = last;
        last.after = p;
    }
}

LinkedHashMap没有重写putVal方法,所以看HashMap的putVal方法可以知道newNode是用来创建一个新节点的。

所以到这里就可以得出结论了。

LinkedHashMap在HashMap的基础上实现了记录顺序的功能。实现的方式是,重写了newNode方法,返回了一个新的数据结构:LinkedHashMapEntry。

LinkedHashMapEntry记录了prev,和next Entry。newNode方法会在put()方法的时候被调用,也就是,每次向HashMap添加元素的时候,就会记录添加的这个节点的prev和prev的next。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值