image.png
LinkedHashMap继承了HashMap,实现了Map接口。由此可以看出LinkedHashMap是一个HashMap,推理出其在HashMap的基础上增加了某些东西。

成员变量

  1. /**
  2. * 双链接列表中的头(最年长的)。
  3. */
  4. transient LinkedHashMap.Entry<K,V> head;
  5. /**
  6. * 双链接列表的尾部(最年轻的)。
  7. */
  8. transient LinkedHashMap.Entry<K,V> tail;
  9. /**
  10. * 此链接哈希映射的迭代排序方法:<tt>true</tt>
  11. * 对于访问顺序,<tt>false</tt>对于插入顺序。
  12. *(即存键值对进去是什么顺序,迭代打印出来的就是什么顺序)。
  13. * @serial
  14. */
  15. final boolean accessOrder;

构造方法

image.png
因为LinkedHashMap继承了HashMap,所以两者底层架构并没有很大的差异(同样都是数组+链表+红黑树)。
两者的差别就在:LinkedHashMap的数据节点多了2个指针:头指针head和尾指针tail
它有5个构造方法,比HashMap多了1个,其他的四个构造方法和HashMap的都差不多
LinkedHashMap(): 无参构造,调用父类HashMap的无参构造(初始容量为16,负载因子为0.75),并且设置accessOrder=false;(即使用插入顺序)

  1. /**
  2. * 构造一个空的插入顺序<tt>LinkedHashMap</tt>实例
  3. * 默认初始容量(16)和负载系数(0.75)。
  4. */
  5. public LinkedHashMap() {
  6. super();
  7. accessOrder = false;
  8. }

LinkedHashMap(int initialCapacity): 调用父类HashMap(initialCapacity),指定初始容量,并且设置accessOrder=false;(即使用插入顺序)

  1. /**
  2. * 构造一个空的插入顺序<tt>LinkedHashMap</tt>实例
  3. * 具有指定的初始容量和默认负载系数(0.75)。
  4. */
  5. public LinkedHashMap(int initialCapacity) {
  6. super(initialCapacity);
  7. accessOrder = false;
  8. }

LinkedHashMap(int initialCapacity, float loadFactor): 调用父类HashMap(initialCapacity, loadFactor),指定初始容量和负载因子,并且设置accessOrder=false;(即使用插入顺序)

  1. /**
  2. *
  3. * 构造具有指定初始容量和负载因子的空插入顺序LinkedHashMap实例。
  4. */
  5. public LinkedHashMap(int initialCapacity, float loadFactor) {
  6. super(initialCapacity, loadFactor);
  7. accessOrder = false;
  8. }

LinkedHashMap(Map<? extends K, ? extends V> m): 调用父类HashMap的空参构造,并且设置accessOrder=false;(即使用插入顺序),调用putMapEntries()方法将map集合转为LinkedHashMap集合。容量由入参map集合决定

  1. /**
  2. * 使用与指定映射相同的映射构造插入顺序的LinkedHashMap实例。
  3. * LinkedHashMap实例是使用默认负载因子(0.75)和初始容量创建的,
  4. * 初始容量足以在指定的映射中保存映射。
  5. *
  6. * @param m the map whose mappings are to be placed in this map
  7. * @throws NullPointerException if the specified map is null
  8. */
  9. public LinkedHashMap(Map<? extends K, ? extends V> m) {
  10. super();
  11. accessOrder = false;
  12. putMapEntries(m, false);
  13. }

LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder): 调用父类HashMap的(initialCapacity, loadFactor),并且设置指定的accessOrder(即使用指定的顺序)。此三个入参的构造方法在HashMap并没有

  1. /**
  2. * 使用指定的初始容量、
  3. * 负载因子和排序模式构造一个空的LinkedHashMap实例。
  4. *
  5. * @param initialCapacity the initial capacity
  6. * @param loadFactor the load factor
  7. * @param accessOrder the ordering mode - <tt>true</tt> for
  8. * access-order, <tt>false</tt> for insertion-order
  9. * @throws IllegalArgumentException if the initial capacity is negative
  10. * or the load factor is nonpositive
  11. */
  12. public LinkedHashMap(int initialCapacity,
  13. float loadFactor,
  14. boolean accessOrder) {
  15. super(initialCapacity, loadFactor);
  16. this.accessOrder = accessOrder;
  17. }

静态内部类

这是增加了head、tail两个指针的类型,继承了HashMap.Node,也就是说拥有HashMap.Node的特性。构造方法调用了HashMap.Node的Node(int hash, K key, V value, Node next)

  1. //普通LinkedHashMap条目的节点子类。
  2. static class Entry<K,V> extends HashMap.Node<K,V> {
  3. Entry<K,V> before, after;
  4. Entry(int hash, K key, V value, Node<K,V> next) {
  5. super(hash, key, value, next);
  6. }
  7. }

linkNodeLast()方法

  1. // link at the end of list
  2. private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
  3. LinkedHashMap.Entry<K,V> last = tail;
  4. tail = p;
  5. if (last == null)
  6. head = p;
  7. else {
  8. p.before = last;
  9. last.after = p;
  10. }
  11. }

访问顺序

  1. HashMap插入的顺序与取出的顺序是不一样的,即HashMap是无序的
  2. LinkedHashMap插入的顺序与取出的顺序是一样的,即LinkedHashMap是有序的

image.png
开启accessOrder访问顺序(即accessOrder=true)
image.png
可以看到被访问后的元素,被置顶了。也就是说,LinedHashMap访问某元素,某元素将被置顶。这样的功能应用在LRU缓存方面。 经常访问的元素就可以很快速地找到了,而不被经常访问的元素可以沉到底甚至可以被覆盖,LinkedHahsMap这样的功能使得它在缓存技术方面发扬光大。
总结
从上面的访问顺序,也可以总结出,遍历LinkedHashMap是按照从链表开始扫描,扫到尾部的。
get元素后,元素被放到链表的尾部,所以每次get后再遍历LinkedHashMap,get的元素都会出现在末尾。