Java LinkedHashMap详解

简介: Java LinkedHashMap详解

1. LinkedHashMap概览



  1. LinkedHashMap类定义
public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>


LinkedHashMap直接继承自HashMap。所以说LinkedHashMap内部的实现也是数组+链表/红黑树实现


  1. LinkedHashMap的Entry


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);
    }
}

LinkedHashMap.Entry继承自HashMap.Node。同时新增了before和after两个字段,用来维护LinkedHashMap中Entry的顺序。LinkedHashMap的entrySet()遍历就是通过该双链表来实现的。


1.LinkedHashMap的成员变量

transient LinkedHashMap.Entry<K,V> head;//指向双链表的头
transient LinkedHashMap.Entry<K,V> tail;//指向双链表的尾
final boolean accessOrder;//访问是否排序,访问包括get和put

LinkedHashMap的构造函数


public LinkedHashMap(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor);
    accessOrder = false;
}
public LinkedHashMap(int initialCapacity) {
    super(initialCapacity);
    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;
}

从定义的构造函数可以看出,相比HashMap只是多了accessOrder的定义。后面我们会详细讲解accessOrder的作用


2. LinkedHashMap接口方法详解



1. V get(Object key)


public V get(Object key) {
    Node<K,V> e;
    //getNode方法是HashMap提供的实现方法,通过Hash值定位下标,然后比对key和hash值是否相等
    if ((e = getNode(hash(key), key)) == null)
        return null;
    //如果设置了accessOrder
    if (accessOrder)
        //这个方法后面重点讲解下 
        afterNodeAccess(e);
    return e.value;
}

2. V put(K key, V value)


put方法完全复用了HashMap的put方法,具体解释可以参考HashMap

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                //如果是覆盖key的value调用afterNodeAccess(e)
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        //如果是新插入的值 会调用afterNodeInsertion
        afterNodeInsertion(evict);
        return null;
    }

newNode,在LinkedHashMap中新插入一个Node,会链接到双链表的尾端


 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中afterNodeAccess(e)、afterNodeInsertion(evict)、afterNodeRemoval()都是空实现。但是在LinkedHashMap中。他们是有具体实现的。接下来我们分别来看下他们的实现


1.afterNodeAccess()

访问包括 get(key) put(key,value) put只限于原来key有value才算访问,新插入的不会调用afterNodeAccess()

void afterNodeAccess(Node<K,V> e) { // 把Entry移动到双链表的尾端
      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;
       }
   }


2.afterNodeInsertion()


当插入了新的节点后,会根据需要去删除双链表头指向的节点

void afterNodeInsertion(boolean evict) { // 如果允许删除在HashMap中最老的Entry。双链表head指向的就是最老的Entry
       LinkedHashMap.Entry<K,V> first;
       if (evict && (first = head) != null && removeEldestEntry(first)) {
           K key = first.key;
           removeNode(hash(key), key, null, false, true);
       }
}
3.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;
   }


3. remove(Object key)

remove(Object key)复用了HashMap的remove(Object key)方法。唯一的区别就是LinkedHashMap删除数据后,会从双链表删除数据


4. entrySet()

entrySet()的功能和HashMap一样也是为了遍历map中的元素。但是LinkedHashMap和HashMap遍历的实现确不相同。HashMap实现遍历的方式是从table数组的0下标开始查找,直到查到一个非空的元素,如果该元素还有链表或者红黑树,遍历之,遍历完接着遍历数组。LinkedHashMap遍历的方式是从双链表的head一直往尾端遍历。他们的区别就是HashMap遍历是无序的。LinkedHashMap遍历是有序的

1. 生成entrySet
public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es;
        return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
}
2.iterator()方法
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) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>) o;
        Object key = e.getKey();
        Node<K,V> candidate = getNode(hash(key), key);
        return candidate != null && candidate.equals(e);
    }
    public final boolean remove(Object o) {
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Object value = e.getValue();
            return removeNode(hash(key), key, value, true, true) != null;
        }
        return false;
}
final class LinkedEntryIterator extends LinkedHashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); }
    }
abstract class LinkedHashIterator {
        LinkedHashMap.Entry<K,V> next;
        LinkedHashMap.Entry<K,V> current;
        int expectedModCount;
        LinkedHashIterator() {
            //从双链表的head开始
            next = head;
            expectedModCount = modCount;
            current = null;
        }
        public final boolean hasNext() {
            return next != null;
        }
        //遍历双链表 从head开始
        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;
        }
    }


相关文章
|
6月前
|
缓存 Java 程序员
Java Review - LinkedHashMap & LinkedHashSet 源码解读
Java Review - LinkedHashMap & LinkedHashSet 源码解读
48 0
Java Review - LinkedHashMap & LinkedHashSet 源码解读
|
11天前
|
存储 Java API
详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
【10月更文挑战第19天】深入剖析Java Map:不仅是高效存储键值对的数据结构,更是展现设计艺术的典范。本文从基本概念、设计艺术和使用技巧三个方面,详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
31 3
|
3月前
|
Java
【Java集合类面试二十】、请介绍LinkedHashMap的底层原理
LinkedHashMap的底层原理是在HashMap的基础上,通过维护一条双向链表来保持键值对的插入和遍历顺序,同时继承HashMap的多数方法并重写部分方法以维护链表。
|
3月前
|
Java
【Java集合类面试十九】、说一说你对LinkedHashMap的理解
LinkedHashMap通过维护一个双向链表来保证key-value对的迭代顺序与插入顺序一致,它提供了HashMap的性能和有序迭代的特性,但相比HashMap性能略低。
|
3月前
|
存储 Java 数据处理
|
3月前
|
存储 缓存 Java
|
6月前
|
存储 缓存 Java
Java LinkedHashMap:保持插入顺序的哈希表解析
Java LinkedHashMap:保持插入顺序的哈希表解析
136 0
|
6月前
|
缓存 算法 Java
认真学习Java集合之LinkedHashMap的实现原理
认真学习Java集合之LinkedHashMap的实现原理
76 0
认真学习Java集合之LinkedHashMap的实现原理
|
11月前
|
存储 算法 Java
史上最全的Java容器集合之LinkedHashMap(源码解读)
史上最全的Java容器集合之LinkedHashMap(源码解读)
324 0
史上最全的Java容器集合之LinkedHashMap(源码解读)
|
存储 算法 Java
Java-数据结构(二)-Map:HashMap、TreeMap、LinkedHashMap
Java-数据结构(二)-Map:HashMap、TreeMap、LinkedHashMap
114 1