Java8源码-LinkedHashMap

LinkedHashMap是HashMap的扩展,维护了一个双向循环链表以实现有序性。它提供了插入顺序和访问顺序的迭代,继承HashMap并实现了Map接口。文章详细介绍了其数据结构、构造方法、内部机制和常用方法,对比了与HashMap的区别。
摘要由CSDN通过智能技术生成

前面已经学习了HashMap源码Hashtable源码,今天开始学习LinkedHashMap。参考的JDK版本为1.8。

LinkedHashMap继承了HashMap,是Map接口的哈希表和链接列表实现。哈希表的功能通过继承HashMap实现了。LinkedHashMap还维护着一个双重链接链表。此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。本文主要讲解双重链接链表的部分,看看它是如何实现有序性的。

数据结构

在分析LinkedHashMap源码之前,有必要了解LinkedHashMap的数据结构,否则很难理解下面的内容。

MarkdownPhotos/master/CSDNBlogs/container/LinkedHashMap/LinkedHashMapDateStructure.jpg

从上图中可以很清楚的看到,HashMap的数据结构是数组+链表+红黑树(since JDK1.8)+ 双重链接列表。数组+链表+红黑树的部分请参考HashMap源码一文中关于数据结构的讲解,HashMap和LinkedHashMap在这部分的实现是几乎相同的。LinkedHashMap为每个Entry添加了前驱和后继,构成了一个双向循环链表,每次向linkedHashMap插入键值对,除了将其插入到哈希表的对应位置之外,还要将其插入到双向循环链表的尾部。

部分顶部注释

Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k, v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)

大意为LinkedHashMap是Map的哈希表和链接列表的实现,具有可预知的迭代顺序。LinkedHashMap和HashMap的不同之处在于它包含一个贯穿于所有entry的双重链接列表。双重链接列表定义了迭代顺序,默认是插入顺序。值得注意的是,如果一个key被重插入,插入顺序不受影响。

层次结构图

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

从中我们可以了解到:

  • LinkedHashMap<K,V>:HashMap是以key-value形式存储数据的
  • extends HashMap<K,V>:继承了HashMap,哈希表部分的功能和HashMap相似。
  • implements Map<K,V>:实现了Map。HashMap已经继承了Map接口,为什么LinkedHashMap还要实现Map接口呢?仔细看过Java容器其他源码的朋友会发现,不仅仅LinkedHashMap这样做,其他实现类也经常这样做。网上的一些看法是这样做可以直观地表达出LinkedHashMap实现了Map。如果大家有其他看法,欢迎留言。

说到直观地展示出一个类的继承实现结构,eclipse的类层次结构图就可以实现这个功能。下图是LinkedHashMap类结构层次图

MarkdownPhotos/master/CSDNBlogs/container/LinkedHashMap/LinkedHashMapTH.jpg

如何查看类层次结构图可以参考我写过的一篇文章:

eclipse-查看继承层次图/继承实现层次图

/**
 * 双向循环链表的头结点
 */
transient LinkedHashMap.Entry<K,V> head;

/**
 * 双向循环链表的尾结点
 */
transient LinkedHashMap.Entry<K,V> tail;

/**
 * 迭代顺序。
 * true代表按访问顺序迭代
 * false代表按插入顺序迭代
 * 
 * @serial
 */
final boolean accessOrder;

这里有必要看下LinkedHashMap的Entry的定义。LinkedHashMap的Entry继承了HashMap的Node,并且每个entry都包含前指针和后指针,这是双向循环链表的特点。

/**
 * LinkedHashMap的Entry继承了HashMap的Node
 */
static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    //构造方法。
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

私有方法

linkNodeLast( LinkedHashMap.Entry<K,V> p)
/**
 * 将指定entry插入到双向链表末尾
 */
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
    LinkedHashMap.Entry<K,V> last = tail;
    //尾指针执行p
    tail = p;
    //如果旧的尾节点指向null,意味着双向循环链表为空,这时头尾指针都要指向p
    if (last == null)
        head = p;
    else {
  //否则将p插入到旧尾节点的后面
        p.before = last;
        last.after = p;
    }
}
// 将src替换为dst
private void transferLinks(LinkedHashMap.Entry<K,V> src,
                           LinkedHashMap.Entry<K,V> dst) {
    LinkedHashMap.Entry<K,V> b = dst.before = src.before;
    LinkedHashMap.Entry<K,V> a = dst.after = src.after;
    //如果src的前指针指向null,说明src为头节点,这时将dst替换为头节点即可
    if (b == null)
        head = dst;
    else//否则,将dst的前指针指向的节点的后指针指向dst
        b.after = dst;
    //如果src的后指针指向null,说明src为尾节点,这时将dst替换为尾节点即可
    if (a == null)
        tail = dst;
    else//否则,将dst的后指针指向的节点的前指针指向dst
        a.before = dst;
}

重写HashMap的方法

reinitialize()
//将linkedHashMap重置到初始化的
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值