LinkedHashMap

1.概述
  • LinkedHashMap继承HashMap,其多种操作都是建立在HashMap操作的基础上。与HashMap不同的是,LinkedHashMap维护了一个Entry的双向链表,保证了插入的Entry的顺序,默认是按照插入顺序,另外一种是访问顺序
2.LinkedHashMap和HashMap的区别
  • HashMap结构图

    在这里插入图片描述

  • LinkedHashMap结构图

在这里插入图片描述

从结构图上看,LinkedHashMap就是在HashMap的基础上,通过双向链表将各个桶中的结点串联起来,这样可以维护元素之间的顺序,而HashMap是不知道结点之间的前后插入顺序的

底层实现数据结构
HashMap

       JDK1.8以前:数组+链表

       JDK1.8以后:数组+链表+红黑树

LinkedHashMap

        在HashMap的基础上再维护了一个双向链表
LinkedHashMap的源码解析
  • 从继承关系我们可以看到,LinkedHashMap继承HashMap,并实现了map接口

     	public class LinkedHashMap<K,V>  extends HashMap<K,V> implements Map<K,V> {
    
  • LinkedHashMapEntry继承HashMap的Entry,他表示一个结点(元素);head是头指针,指向第一个结点;tail是尾指针,指向最后一个结点;accessOrder用来控制访问顺序,若为true则采用访问排序,若为false则采用插入排序,默认采用插入排序

           /**
          * 双向链表的头
          */
         transient LinkedHashMapEntry<K, V> head;
     
         /**
          * 双向链表的尾
          */
         transient LinkedHashMapEntry<K, V> tail;
    
    
     	    /**
     	     * 一个条件变量,它控制了是否在get操作后需要将新的get的节点重新放到链表的尾部
     	     * LinkedHashMap可以维持了插入的顺序,但是这个顺序不是不变的,可以被get操作打乱。
     	     *
     	     * @serial
     	     */
         final boolean accessOrder;
    
    
    
         /**
          * LinkedHashMap的entry包装类  继承了HashMap的Node,也就是在上面添加了前后结点
          *
          * @param <K>
          * @param <V>
          */
         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);
             }
         }
    
  • 从构造函数来看,并没有做特别操作,只是将accessOrder设置为false,即采用插入排序

     	    //无参数构造函数 默认使用无参数
     	    public LinkedHashMap() {
     	        super();
     	        accessOrder = false;
     	    }
     	
     	    //指定初始容量
     	    public LinkedHashMap(int initialCapacity) {
     	        super(initialCapacity);
     	        accessOrder = false;
     	    }
     	
     	    public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
     	        super(initialCapacity, loadFactor);
     	        accessOrder = false;
     	    }
     	
     	
     	
     	    public LinkedHashMap(Map<? extends K, ? extends V> m) {
     	        super();
     	        accessOrder = false;
     	        putMapEntries(m, false);
     	    }
    
  • LinkedHashMap没有重写put()/putAll()方法,因此其插入方式是和HashMap一样的,只是重写了newNode()/newTreeNode()来构建新结点/树结点,在构建新结点后调用linkNodeLast()将新结点放入双向链表正确的位置,之后再将新创建的结点返回给父类hashMap进行插入操作

        //覆盖了父类的newNode方法
         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);
             return p;
         }
     
         //
         TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
             TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
             linkNodeLast(p);
             return p;
         }
     
     
     
     
         private void linkNodeLast(LinkedHashMapEntry<K,V> p) {
             //1.将双向链表tail赋值给临时变量p
             LinkedHashMapEntry<K,V> last = tail;
             //将p赋值给尾结点tail
             tail = p;
             //若尾结点是空  表示是第一个结点 那么就是头结点
             if (last == null)
                 head = p;
             //若尾结点不为空  则当前结点的前一个结点是之前尾结点,之前尾结点的下一个结点是当前结点
             else {
                 p.before = last;
                 last.after = p;
             }
         }
    
  • 重写了afterNodeInsertion(),该方法是在插入一个新结点完成之后调用,在这里可以设置是否当缓存满了之后,移除头结点。

        void afterNodeInsertion(boolean evict) { // possibly remove eldest
            LinkedHashMapEntry<K,V> first;
            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;
    	    }
    
  • LinkedHashMap没有重写remove(),其移除规则还是按照hashMap移除规则,只是重写了afterNodeRemoval(),在方法中的会判断删除的结点有没有前结点,若没有则表示是头结点,那么将删除结点的下一个结点作为头结点,且设置它的前一个结点为null;若不是头结点则再判断当前结点是否为尾结点,那么将删除结点的前一个结点作为尾结点,且设置它的下一个结点为null;若既不是头结点也不是尾结点, 那就是中间结点,那么让删除结点前一个结点的after指向删除结点的后一个结点,让删除结点后的下一个结点的before只想删除结点的前一个结点

           /**
            * 若移除指定结点,判断当前结点不是不是
            * @param e
            */
           void afterNodeRemoval(Node<K,V> e) { // unlink
       
               //获取移除结点的前后结点
               LinkedHashMapEntry<K,V> p = (LinkedHashMapEntry<K,V>)e,
               b = p.before,
               a = p.after;
       
               //将当前结点的前后结点置空
               p.before = p.after = null;
               //若当前结点的前结点为空 表示当前结点是头结点 那么将当前结点的下一个结点变成头结点
               if (b == null)
                   head = a;
               //若当前结点的前结点不为空 表示当前结点是中间的一个结点,
               //那么当前结点的前一个结点的after是直接指向当前结点的下一个结点的,那么当前结点将会被移除到整个链表中
               else
                   b.after = a;
       
               //若当前结点的尾结点是空 表示当前结点是最后个结点,那么尾结点就直接是当前结点的前一个结点
               if (a == null)
                   tail = b;
               //当前结点的下一个结点的前结点是指向当前结点的前面结点的
               else
                   a.before = b;
           }
    
  • 重写清除方法,将头尾结点置空

           /**
            * 头尾结点置空
             */
           public void clear() {
               super.clear();
               head = tail = null;
           }
    
  • 通过Set遍历获取所有结点的key值,其主要是通过iterator来实现,不断遍历结点,然后取其key值

       	    public Set<K> keySet() {
       	        Set<K> ks = keySet;
       	        if (ks == null) {
       	            ks = new LinkedKeySet();
       	            keySet = ks;
       	        }
       	        return ks;
       	    }
       	
       	    final class LinkedKeySet extends AbstractSet<K> {
       	        public final int size()                 { return size; }
       	        public final void clear()               {  LinkedHashMap.this.clear(); }
       	        public final Iterator<K> iterator() {
       	            return new LinkedKeyIterator();
       	        }
       	        public final boolean contains(Object o) { return containsKey(o); }
       	        public final boolean remove(Object key) {
       	            return removeNode(hash(key), key, null, false, true) != null;
       	        }
       	        public final Spliterator<K> spliterator()  {
       	            return Spliterators.spliterator(this, Spliterator.SIZED |
       	                    Spliterator.ORDERED |
       	                    Spliterator.DISTINCT);
       	        }
       	        public final void forEach(Consumer<? super K> action) {
       	            if (action == null)
       	                throw new NullPointerException();
       	            int mc = modCount;
       	            // Android-changed: Detect changes to modCount early.
       	            for (LinkedHashMapEntry<K,V> e = head; (e != null && modCount == mc); e = e.after)
       	                action.accept(e.key);
       	            if (modCount != mc)
       	                throw new ConcurrentModificationException();
       	        }
       	
       	
       	    }
    
    
       final class LinkedKeyIterator extends LinkedHashIterator
               implements Iterator<K> {
           public final K next() { return nextNode().getKey(); }
       }
    
    
     abstract class LinkedHashIterator {
           LinkedHashMapEntry<K,V> next;
           LinkedHashMapEntry<K,V> current;
           int expectedModCount;
    
    
           LinkedHashIterator() {
               next = head;
               expectedModCount = modCount;
               current = null;
           }
    
           public final boolean hasNext() {
               return next != null;
           }
    
           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;
           }
       }
    
  • 通过Collection遍历获取所有结点的value值,其主要是通过iterator来实现,不断遍历结点,然后取其value值

       public Collection<V> values() {
              Collection<V> vs = values;
              if (vs == null) {
                  vs = new LinkedValues();
                  values = vs;
              }
              return vs;
          }
      
          final class LinkedValues extends AbstractCollection<V> {
              public final int size()                 { return size; }
              public final void clear()               { LinkedHashMap.this.clear(); }
              public final Iterator<V> iterator() {
                  return new LinkedValueIterator();
              }
              public final boolean contains(Object o) { return containsValue(o); }
              public final Spliterator<V> spliterator() {
                  return Spliterators.spliterator(this, Spliterator.SIZED |
                                                  Spliterator.ORDERED);
              }
              public final void forEach(Consumer<? super V> action) {
                  if (action == null)
                      throw new NullPointerException();
                  int mc = modCount;
                  // Android-changed: Detect changes to modCount early.
                  for (LinkedHashMapEntry<K,V> e = head; (e != null && modCount == mc); e = e.after)
                      action.accept(e.value);
                  if (modCount != mc)
                      throw new ConcurrentModificationException();
              }
          }
      
      
         final class LinkedValueIterator extends LinkedHashIterator
              implements Iterator<V> {
              public final V next() { return nextNode().value; }
          }
      
      
        abstract class LinkedHashIterator {
              LinkedHashMapEntry<K,V> next;
              LinkedHashMapEntry<K,V> current;
              int expectedModCount;
      
              LinkedHashIterator() {
                  next = head;
                  expectedModCount = modCount;
                  current = null;
              }
      
              public final boolean hasNext() {
                  return next != null;
              }
      
              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;
              }
          }
    
  • 通过Collection遍历获取所有结点,其主要是通过iterator来实现

      public Set<Map.Entry<K,V>> entrySet() {
            Set<Map.Entry<K,V>> es;
            return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
        }
    
        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;
            }
            public final Spliterator<Map.Entry<K,V>> spliterator() {
                return Spliterators.spliterator(this, Spliterator.SIZED |
                                                Spliterator.ORDERED |
                                                Spliterator.DISTINCT);
            }
            public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
                if (action == null)
                    throw new NullPointerException();
                int mc = modCount;
                // Android-changed: Detect changes to modCount early.
                for (LinkedHashMapEntry<K,V> e = head; (e != null && mc == modCount); e = e.after)
                    action.accept(e);
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    
        // Map overrides
    
        public void forEach(BiConsumer<? super K, ? super V> action) {
            if (action == null)
                throw new NullPointerException();
            int mc = modCount;
            // Android-changed: Detect changes to modCount early.
            for (LinkedHashMapEntry<K,V> e = head; modCount == mc && e != null; e = e.after)
                action.accept(e.key, e.value);
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    
        public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
            if (function == null)
                throw new NullPointerException();
            int mc = modCount;
            // Android-changed: Detect changes to modCount early.
            for (LinkedHashMapEntry<K,V> e = head; modCount == mc && e != null; e = e.after)
                e.value = function.apply(e.key, e.value);
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    
    
    
        final class LinkedEntryIterator extends LinkedHashIterator
            implements Iterator<Map.Entry<K,V>> {
            public final Map.Entry<K,V> next() { return nextNode(); }
        }
    
    
        abstract class LinkedHashIterator {
            LinkedHashMapEntry<K,V> next;
            LinkedHashMapEntry<K,V> current;
            int expectedModCount;
    
            LinkedHashIterator() {
                next = head;
                expectedModCount = modCount;
                current = null;
            }
    
            public final boolean hasNext() {
                return next != null;
            }
    
            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;
            }
        }
    
  • 通过getNode()获取结点,若获取到结点后判断是否需要进行访问排序

           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;
           }
       
       
       
           void afterNodeAccess(Node<K,V> e) { // move node to last
               LinkedHashMapEntry<K,V> last;
               if (accessOrder && (last = tail) != e) {
                   LinkedHashMapEntry<K,V> p =
                       (LinkedHashMapEntry<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基础上维护了一个双向链表,它将插入的结点的前后连接起来,可以进行排序操作
  • LinkedHashMap本身并不实现插入操作,只是重写了创建新结点/树结点的方法,在创建结点后先插入到双向链表,之后返回给HashMap进行插入操作,最后插入完成之后还可以操作当缓存满了之后删除头结点的操作
  • LinkedHashMap本身并不实现删除操作,只是重写了删除之后的方法,在hashMap删除结点之后将结点从双向链表中删除掉
  • LinkedHashMap重写了get(),获取到结点之后可以根据条件进行排序操作
  • 重写了keySet()/values()/entrySet()遍历双向链表的结点来实现迭代
  • 重写clear(),在HashMap清除结点之后,将双向链表清空。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值