LinkedHashMap

186 阅读2分钟
  • 继承HashMap
  • 有序,基于双向链表保持插入顺序或访问顺序
  • 访问顺序可实现LRU缓存,会将新操作的对象放在队尾,删除时会先删除队首
  • 非线程安全

主要属性和构造器

public class LinkedHashMap<K,V>
    extends HashMap<K,V>  implements Map<K,V>{
    // 结点,个人理解继承过来是为了便于访问,不继承应该也可以访问
    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);
        }
    }
    // 双向链表的头结点,最老结点
    transient LinkedHashMap.Entry<K,V> head;

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

    //遍历顺序: true 访问顺序、 false 插入顺序
    final boolean accessOrder;   
    
    // 默认使用插入顺序
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }
    
    // 指定初始化容量
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }
    
    // 指定初始化容量和加载因子
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }
    
    // 指定初始化容量、加载因子、访问顺序
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }
    
    // 通过特定map实例化
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }
}

方法

Put 方法

Put方法没重写,调用父类的方法 重写了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;
    //添加第一个结点,头尾都是p
    if (last == null)
        head = p;
    else {
        p.before = last;
        last.after = p;
    }
}

Get 方法

    // 返回null 有两种可能:一、没有此key,二、此key对应的值为空
    // 可通过containsKey()方法区分
    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        // 如果是访问顺序,将结点移动到尾部
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }
    
 // 最常用的将其放在链表的最后,不常用的放在链表的最前
 // move node to last 移动到链表尾部
 void afterNodeAccess(Node<K,V> e) { 
        LinkedHashMap.Entry<K,V> last;
        // 访问顺序,e不是尾结点
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
				(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            // e 为头结点,将e的后继结点设为头结点
            if (b == null)
                head = a;
            //将e的前驱结点和后继结点连接
            else
                b.after = a;
            // e 非尾结点,将e的前驱结点设为头结点  
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

Remove 方法

通过重写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;
}

最后推荐一下我的公众号,淘宝购物返现省钱

永久海报.jpg