JDK源码之LinkedHashMap解析

1. 简介
  • HashMap没有保证散列表的存取顺序,而LinkedHashMap继承了HashMap,并在此基础上保证了元素的存取顺序,可以按照访问顺序插入顺序两种顺序进行访问
  • LinkedHashMap继承了HashMap,因此具有HashMap的所有特性。
  • LinkedHashMap内部维护了一个双向链表,在每次往哈希表中put数据时,也会往双向链表中插入一份数据,虽然增加了一些时间和空间的开销,但是可以保证顺序。
2. 继承体系

在这里插入图片描述

//继承了HashMap,实现了Map接口
public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>

HashMap的内部是数组+链表+红黑树,LinkedHashMap继承于HashMap,只是又在HashMap的基础上又添加了一个双向链表,虽然耗费了一些时间和空间,但是能够保证存取顺序(按照插入顺序或是访问顺序)。HashMap在存储时只需要维护在哈希表中的存储,而LinkedHashMap不仅需要维护在哈希表中的存储,还需要维护在双向链表中的存储,因此会慢一些。

3. 成员变量
//序列化版本号
private static final long serialVersionUID = 3801124242820219131L;

//被transient修饰的变量不能被序列化
//双向链表的头结点
transient LinkedHashMap.Entry<K,V> head;

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

/*
accessOrder为true,表明在访问时按照访问顺序输出
accessOrder为false,表明在访问时按照插入顺序输出
如果不传这个accessOrder参数,默认为false
*/
final boolean accessOrder;
4. 构造方法
//指定初始容量和负载因子
public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);             //调用父类HashMap的构造方法
        accessOrder = false;                            //输出时按照插入顺序访问
    }
//制定初始容量
public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);                         //调用父类HashMap的构造方法
        accessOrder = false;
    }
//无参构造方法
public LinkedHashMap() {                                
        super();
        accessOrder = false;
    }
//传入一个集合的构造方法
public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }
//指定初始容量和负载因子和按照什么顺序访问
public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }
5. 内部类
LinkedHashMap中的内部类
//继承了HashMap中的内部类Node,添加了before, after,用来维护双向链表的首尾节点
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);
        }
    }
//HashMap中的内部类
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
6. 成员方法
put(key, value)
/*
LinkedHashMap中并没有重写父类HashMap的put方法,使用时直接调用父类的put方法,在构建时构建的是LinkedHashMap.Entry而不是Node
*/
//但是LinkedHashMap重写了newNode方法,在创建新结点时不仅插入到哈希表中,也插入到双链表中
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p);				//插入到双链表的结尾
        return p;
    }
//将新增的结点添加到双链表的结尾   
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;								//重置尾结点
        if (last == null)						
            head = p;							//插入前链表为空,则说明插入的结点在插入之后也是头结点
        else {
            p.before = last;
            last.after = p;
        }
    }
    

// HashMap中的newNode方法
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
    return new Node<>(hash, key, value, next);
}

以及HashMap专门留给LinkedHashMap使用的afterNodeAccess,afterNodeInsertion,afterNodeRemoval回调方法

afterNodeInsertion,removeEldestEntry是构建LruCache用到的方法,在这里可以忽略它们

LinkedHashMap中
//将结点添加到双链表的结尾
void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

//根据evict决定是否要删除元素,如果实现LruCache会用到这个方法
void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry<K,V> first;
        //LinkedHashMap默认返回false,不删除结点
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    } 
    
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;	//默认返回false
    }
//在移除结点时会触发回调,从双向链表中删除
void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.before = p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a == null)
            tail = b;
        else
            a.before = b;
    }
HashMap中
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
remove()

LinkedHashMap并没有重写父类HashMap的remove方法,而是使用的父类的remove方法

get()
//LinkedHashMap重写了HashMap的get方法和afterNodeAccess方法
public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    if (accessOrder)		//如果accessOrder为true,说明是按照访问顺序访问,将该结点添加到双向链表的结尾
        afterNodeAccess(e);
    return e.value;
}
//将结点添加到双链表的结尾
void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }
    
遍历

重写了entrySet方法

public Set<Map.Entry<K,V>> entrySet() {
    Set<Map.Entry<K,V>> es;
    return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
}
afterNodeRemoval()
//在移除结点时会触发回调,从双向链表中删除
void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.before = p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a == null)
            tail = b;
        else
            a.before = b;
    }
afterNodeInsertion()
//根据evict决定是否要删除元素,如果实现LruCache会用到这个方法,删除最古老的元素
//
void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry<K,V> first;
        //LinkedHashMap默认返回false,不删除结点
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
//removeEldestEntry(first)是否删除最古老元素,LinkedHashMap默认返回false,也就是不删除最古老元素
afterNodeAccess()

在添加元素或访问元素时后调用这个方法,在访问时如果accessOrder为true,会将访问的元素添加到双链表的结尾

void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }
总结
  • LinkedHashMap继承自HashMap,具有HashMap的所有属性

  • LinkedHashMap是在HashMap的基础上添加了一个双链表,用来保证取出时的顺序

  • 如果accessOrder为false(LinkedHashMap默认为false),则取出时按照存入顺序取出

    如果accessOrder为true,则取出时按照访问顺序取出(每访问一个结点,就将该结点移至双链表的结尾)

  • LinkedHashMap的很多方法都是使用的HashMap的方法。

  • LinkedHashMap可以使用LRU的缓存淘汰策略

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值