1、LinkedHashMap-底层数据结构

  1. 1LinkedHashMap-底层数据结构:与HanshMap一样,只是实现细节上有所区别。
  2. 2LinkedHashMap-属性有:
  3. /**
  4. * 双向链表的头节点
  5. */
  6. transient LinkedHashMap.Entry<K,V> head;
  7. /**
  8. * 双向链表的尾节点
  9. */
  10. transient LinkedHashMap.Entry<K,V> tail;
  11. /**
  12. * Ttrue表示最近最少使用次序(访问顺序),false表示插入顺序
  13. *
  14. * @serial
  15. */
  16. final boolean accessOrder;

2、LinkedHashMap-构造方法

  1. 1LinkedHashMap有多个构造方法:所有构造方法都是通过调用父类的构造方法来创建对象的。
  2. 2、源代码:
  3. /**
  4. 无参构造函数:
  5. LinkedHashMap存储数据是有序的,而且分为两种:插入顺序(默认值)和访问顺序。
  6. */
  7. public LinkedHashMap() {
  8. //.super()调用了父类HashMap的构造方法,其实就是根据初始容量、负载因子去初始化Entry[] table
  9. super();
  10. //把accessOrder设置为false-存储顺序为插入顺序,默认值
  11. accessOrder = false;
  12. }
  13. /**
  14. 指定初始容量的构造函数
  15. */
  16. public LinkedHashMap(int initialCapacity) {
  17. super(initialCapacity);
  18. accessOrder = false;
  19. }
  20. /**
  21. 指定初始容量和加载因子的构造函数
  22. */
  23. public LinkedHashMap(int initialCapacity, float loadFactor) {
  24. super(initialCapacity, loadFactor);
  25. accessOrder = false;
  26. }
  27. /**
  28. 传入Map为参数的构造函数
  29. */
  30. public LinkedHashMap(Map<? extends K, ? extends V> m) {
  31. super();
  32. accessOrder = false;
  33. putMapEntries(m, false);
  34. }
  35. /**
  36. 指定初始容量、加载因子、存储顺序的构造函数
  37. */
  38. public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
  39. super(initialCapacity, loadFactor);
  40. this.accessOrder = accessOrder;
  41. }

3、LinkedHashMap-Entry类

  1. 1Entry类-简介
  2. 1-1JDK8之前
  3. 1-1-1LinkedHashMapEntry元素继承HashMapEntry,提供了双向链表的功能。在上述HashMap的构造器中,最后会调用init()方法,进行相关的初始化,这个方法在HashMap的实现中并无意义,只是提供给子类实现相关的初始化调用。
  4. 1-1-2LinkedHashMap重写了init()方法,在调用父类的构造方法完成构造后,进一步实现了对其元素Entry的初始化操作。
  5. 1-2JDK8之后
  6. 1-2-1LinkedHashMapEntry元素继承HashMapNode,HashMap中的Node实现了Entry
  7. 1-2-2、源代码:
  8. /**
  9. * 继承了HashMap的Node,Node基础上添加了before和after两个指针,
  10. */
  11. static class Entry<K,V> extends HashMap.Node<K,V> {
  12. //用于维护Entry插入的先后顺序的,即:用于维护双向链表。
  13. Entry<K,V> before, after;
  14. //Node<K,V> next : 是用于维护HashMap指定table位置上连接的Entry的顺序的
  15. Entry(int hash, K key, V value, Node<K,V> next) {
  16. super(hash, key, value, next);
  17. }
  18. }
  19. /**
  20. * Basic hash bin node, used for most entries.
  21. * TreeNode子类,并在LinkedHashMap中为其Entry子类创建
  22. */
  23. static class Node<K,V> implements Map.Entry<K,V> {
  24. final int hash;
  25. final K key;
  26. V value;
  27. Node<K,V> next;
  28. Node(int hash, K key, V value, Node<K,V> next) {
  29. this.hash = hash;
  30. this.key = key;
  31. this.value = value;
  32. this.next = next;
  33. }
  34. public final K getKey() { return key; }
  35. public final V getValue() { return value; }
  36. public final String toString() { return key + "=" + value; }
  37. public final int hashCode() {
  38. return Objects.hashCode(key) ^ Objects.hashCode(value);
  39. }
  40. public final V setValue(V newValue) {
  41. V oldValue = value;
  42. value = newValue;
  43. return oldValue;
  44. }
  45. public final boolean equals(Object o) {
  46. if (o == this)
  47. return true;
  48. if (o instanceof Map.Entry) {
  49. Map.Entry<?,?> e = (Map.Entry<?,?>)o;
  50. if (Objects.equals(key, e.getKey()) &&
  51. Objects.equals(value, e.getValue()))
  52. return true;
  53. }
  54. return false;
  55. }
  56. }
  57. 2、当你插入元素时它会将节点插入双向链表的链尾,如果key重复,则也会将节点移动至链尾,当用get()方法获取value时也会将节点移动至链尾。

4、LinkedHashMap-添加元素

  1. 1、添加元素:.put()方法。
  2. 1-1LinkedHashMapput()方法是调用的HashMapput()方法。???
  3. HashMap中.put()
  4. public V put(K key, V value) {
  5. return putVal(hash(key), key, value, false, true);
  6. }
  7. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
  8. Node<K,V>[] tab; Node<K,V> p; int n, i;
  9. if ((tab = table) == null || (n = tab.length) == 0)
  10. n = (tab = resize()).length;
  11. if ((p = tab[i = (n - 1) & hash]) == null)
  12. //LinkedHashMap中重写了.newNode()方法
  13. tab[i] = newNode(hash, key, value, null);
  14. else {
  15. Node<K,V> e; K k;
  16. if (p.hash == hash &&
  17. ((k = p.key) == key || (key != null && key.equals(k))))
  18. e = p;
  19. else if (p instanceof TreeNode)
  20. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  21. else {
  22. for (int binCount = 0; ; ++binCount) {
  23. if ((e = p.next) == null) {
  24. p.next = newNode(hash, key, value, null);
  25. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  26. treeifyBin(tab, hash);
  27. break;
  28. }
  29. if (e.hash == hash &&
  30. ((k = e.key) == key || (key != null && key.equals(k))))
  31. break;
  32. p = e;
  33. }
  34. }
  35. if (e != null) { // existing mapping for key
  36. V oldValue = e.value;
  37. if (!onlyIfAbsent || oldValue == null)
  38. e.value = value;
  39. //在节点被访问后移动链尾,LinkedHashMap中实现了该方法
  40. afterNodeAccess(e);
  41. return oldValue;
  42. }
  43. }
  44. ++modCount;
  45. if (++size > threshold)
  46. resize();
  47. //完成新数据put后调用,LinkedHashMap实现了该方法
  48. afterNodeInsertion(evict);
  49. return null;
  50. }
  51. 2、源代码:
  52. //LinkedHashMap中重写了.newNode()方法
  53. Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
  54. LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e);
  55. //将节点插入链尾
  56. linkNodeLast(p);
  57. return p;
  58. }
  59. // link at the end of list
  60. private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
  61. LinkedHashMap.Entry<K,V> last = tail;
  62. tail = p;2
  63. // 如果链尾为空,则双向链表为空,则p即为头结点也为尾节点
  64. if (last == null)
  65. head = p;
  66. else {
  67. //否则的话修改指针,让之前链尾的after指针指向p,p的before指向之前链尾
  68. p.before = last;
  69. last.after = p;
  70. }
  71. }
  72. /**
  73. * .afterNodeAccess()方法执行过程解析:
  74. * p将引用移除
  75. * b | p | a
  76. * ------------- | ------------- | -------------
  77. * |before| after| <==|==> |before| after| <==|==> |before| after|
  78. * ------------- | ------------- | -------------
  79. *
  80. * 1.b为NULL时,则a变为头结点
  81. * head
  82. * a p
  83. * (b) ------------- -------------
  84. * NULL <------ |before| after| ...... |before| after| (p最后将插入链尾)
  85. * ------------- -------------
  86. * 2.a为NULL时,则b变为链尾节点
  87. *
  88. * tail
  89. * b p
  90. * ------------- (a) -------------
  91. * |before| after| -------> NULL ...... |before| after| (p最后将插入链尾)
  92. * ------------- -------------
  93. * 3.a,b都为NULL时,p即为头结点,又为尾节点
  94. *
  95. * 因为p前后都没有元素,则双向链表中只有p一个节点
  96. *
  97. */
  98. //在节点被访问后移动链尾,LinkedHashMap中实现了HashMap.afterNodeAccess()方法
  99. //在按插入顺序时,在有相同key时,对当前节点将其移到链表末尾。
  100. //通过before、after指针,将新访问节点链接为链表尾节点
  101. void afterNodeAccess(Node<K,V> e) { // move node to last
  102. LinkedHashMap.Entry<K,V> last;
  103. //accessOrder为true && 访问节点不等于为节点
  104. if (accessOrder && (last = tail) != e) {
  105. LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
  106. //因为要移动到链尾,所以先至尾指针为空
  107. p.after = null;
  108. //如果前面没有元素,则p之前为头结点,直接让a成为头结点
  109. if (b == null)
  110. head = a;
  111. else
  112. // 否则b的尾指针指向a
  113. b.after = a;
  114. //如果a不为空,则a的头指针指向b
  115. if (a != null)
  116. a.before = b;
  117. else
  118. //否则 p之前就为尾指针,则令b成为尾指针
  119. last = b;
  120. //如果双向链表中只有p一个节点,则令p即为头结点,也为尾节点
  121. if (last == null)
  122. head = p;
  123. else {
  124. //否则 将p插入链尾
  125. p.before = last;
  126. last.after = p;
  127. }
  128. tail = p;
  129. ++modCount;
  130. }
  131. }
  132. //节点插入,完成新数据put后调用
  133. //removeEldestEntry()默认是返回false的,需要子类继承重写removeEldestEntry()方法。
  134. void afterNodeInsertion(boolean evict) { // possibly remove eldest
  135. LinkedHashMap.Entry<K,V> first;
  136. if (evict && (first = head) != null && removeEldestEntry(first)) {
  137. K key = first.key;
  138. //当插入时,将双向链表的头结点移除
  139. removeNode(hash(key), key, null, false, true);
  140. }
  141. }
  142. //removeEldestEntry()默认是返回false的,需要子类继承重写removeEldestEntry()方法。
  143. //通过覆盖此方法可实现不同策略的缓存,实现缓存机制【!!!】
  144. /**
  145. LinkedHashMap自带的移除最头(最老)数据的方法:
  146. removeElestEntry用于定义删除最老元素的规则。一旦需要删除最老节点,那么将会调用removeNode删除节点。 举个例子,如果一个链表只能维持100个元素,那么当插入了第101个元素时,以如下方式重写removeEldestEntry的话,那么将会删除最老的一个元素。
  147. */
  148. protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
  149. return false;
  150. }

5、LinkedHashMap-获取元素

  1. 1、获取元素:.get()方法:LinkedHashMap重写了get()方法,实现了LRU
  2. 2、源代码:
  3. public V get(Object key) {
  4. Node<K,V> e;
  5. //.getNode(),调用父类HashMap的.getNode()方法
  6. if ((e = getNode(hash(key), key)) == null)
  7. return null;
  8. // accessOder为true时,被访问的节点被置于双向链表尾部
  9. if (accessOrder)
  10. afterNodeAccess(e);
  11. return e.value;
  12. }

6、LinkedHashMap-删除元素

  1. 1、获取元素:.remove()方法:调用的HashMapremove()方法,LinkedHashMap中重写了.afterNodeRemoval()方法。
  2. 2、源代码:
  3. //HashMap.removeNode()
  4. final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue,
  5. boolean movable) {
  6. Node<K,V>[] tab; Node<K,V> p; int n, index;
  7. if ((tab = table) != null && (n = tab.length) > 0 &&
  8. (p = tab[index = (n - 1) & hash]) != null) {
  9. Node<K,V> node = null, e; K k; V v;
  10. if (p.hash == hash &&
  11. ((k = p.key) == key || (key != null && key.equals(k))))
  12. node = p;
  13. else if ((e = p.next) != null) {
  14. if (p instanceof TreeNode)
  15. node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
  16. else {
  17. do {
  18. if (e.hash == hash &&
  19. ((k = e.key) == key ||
  20. (key != null && key.equals(k)))) {
  21. node = e;
  22. break;
  23. }
  24. p = e;
  25. } while ((e = e.next) != null);
  26. }
  27. }
  28. if (node != null && (!matchValue || (v = node.value) == value ||
  29. (value != null && value.equals(v)))) {
  30. if (node instanceof TreeNode)
  31. ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
  32. else if (node == p)
  33. tab[index] = node.next;
  34. else
  35. p.next = node.next;
  36. ++modCount;
  37. --size;
  38. //回调从双向链表中移除node,用于将节点从双向链表移除。LinkedHashMap实现了该方法。
  39. afterNodeRemoval(node);
  40. return node;
  41. }
  42. }
  43. return null;
  44. }
  45. //LinkedHashMap实现了HashMap中的afterNodeRemoval()方法
  46. //在该方法中完成了移除被删除节点的操作
  47. void afterNodeRemoval(Node<K,V> e) { // unlink
  48. LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
  49. // 将 p 节点的前驱后后继引用置空
  50. p.before = p.after = null;
  51. // b 为 null,表明 p 是头节点
  52. if (b == null)
  53. head = a;
  54. else
  55. b.after = a;
  56. // a 为 null,表明 p 是尾节点
  57. if (a == null)
  58. tail = b;
  59. else
  60. a.before = b;
  61. }