简介

可以看到它继承自HashMap,并实现了Map接口。LinkedHashMap与HashMap的主要区别在于,LinkedHashMap维护了一个运行于所有条目的双向链表。这个链表定义了迭代顺序,通常就是插入顺序,或者是访问顺序。  LinkedHashMap的构造函数接受一个名为accessOrder的布尔值参数,用于指定迭代顺序。如果accessOrder为false,则使用插入顺序;如果为true,则使用访问顺序。在访问顺序模式下,调用get和put方法会将相应的条目移动到双向链表的末尾。这种特性使得LinkedHashMap非常适合用来构建LRU(最近最少使用)缓存。  LinkedHashMap还提供了一个名为removeEldestEntry()的方法,它可以被覆盖以移除最老的条目。这个方法默认返回false,即不移除最老的条目。如果需要根据某种策略移除最老的条目,可以创建LinkedHashMap的子类,并覆盖这个方法。  LinkedHashMap的性能通常略低于HashMap,因为维护链表需要额外的资源。然而,LinkedHashMap的迭代性能更高,因为迭代时间仅与实际条目数有关,而与容量无关。在HashMap中,迭代时间与容量有关。
重点属性
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | transient LinkedHashMap.Entry<K,V> head;
 
 
 transient LinkedHashMap.Entry<K,V> tail;
 
 
 final boolean accessOrder;
 
 
 static final int PUT_NORM = 0;
 
 static final int PUT_FIRST = 1;
 
 static final int PUT_LAST = 2;
 
 transient int putMode = PUT_NORM;
 
 | 
构造器
由于继承自HashMap,LinkedHashMap的构造器都是用父类的构造器去构造对象,只是多了些自己的属性:accessOrder,用来确认插入顺序还是访问顺序
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | 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(int initialCapacity,
 float loadFactor,
 boolean accessOrder) {
 super(initialCapacity, loadFactor);
 this.accessOrder = accessOrder;
 }
 
 
 | 
关键方法
put方法
LinkedHashMap的put方法和父类HashMap是一致的,只是重写了其中的几个方法。HashMap的基本类型是Node,而LinkedHashMap的基本数据类型是继承自Node的Entry,同时多了before和after指针。
| 12
 3
 4
 5
 6
 
 | 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);
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 
 | 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)
 treeifyBin(tab, hash);
 break;
 }
 if (e.hash == hash &&
 ((k = e.key) == key || (key != null && key.equals(k))))
 break;
 p = e;
 }
 }
 if (e != null) {
 V oldValue = e.value;
 if (!onlyIfAbsent || oldValue == null)
 e.value = value;
 
 afterNodeAccess(e);
 return oldValue;
 }
 }
 ++modCount;
 if (++size > threshold)
 resize();
 
 afterNodeInsertion(evict);
 return null;
 }
 
 | 
我们看下newNode方法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
 LinkedHashMap.Entry<K,V> p =
 new LinkedHashMap.Entry<>(hash, key, value, e);
 linkNodeAtEnd(p);
 return p;
 }
 
 private void linkNodeAtEnd(LinkedHashMap.Entry<K,V> p) {
 if (putMode == PUT_FIRST) {
 LinkedHashMap.Entry<K,V> first = head;
 head = p;
 if (first == null)
 tail = p;
 else {
 p.after = first;
 first.before = p;
 }
 } else {
 LinkedHashMap.Entry<K,V> last = tail;
 tail = p;
 if (last == null)
 head = p;
 else {
 p.before = last;
 last.after = p;
 }
 }
 }
 
 | 
现在看下插入后的逻辑,这个方法主要是执行是否删除头部Node的逻辑。removeEldestEntry方法默认返回false,默认是不删除的,如果需要这样的动作就需要我们重写removeEldestEntry()方法
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | void afterNodeInsertion(boolean evict) { LinkedHashMap.Entry<K,V> first;
 
 if (evict && (first = head) != null && removeEldestEntry(first)) {
 K key = first.key;
 
 removeNode(hash(key), key, null, false, true);
 }
 }
 
 | 
get方法
get方法和HashMap的get方法是一样的,只是多了一段是否要访问后排序的逻辑
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 
 | public V get(Object key) {Node<K,V> e;
 
 if ((e = getNode(key)) == null)
 return null;
 
 if (accessOrder)
 afterNodeAccess(e);
 return e.value;
 }
 
 void afterNodeAccess(Node<K,V> e) {
 LinkedHashMap.Entry<K,V> last;
 LinkedHashMap.Entry<K,V> first;
 if ((putMode == PUT_LAST || (putMode == PUT_NORM && 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;
 } else if (putMode == PUT_FIRST && (first = head) != e) {
 
 LinkedHashMap.Entry<K,V> p =
 (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
 p.before = null;
 if (a == null)
 tail = b;
 else
 a.before = b;
 if (b != null)
 b.after = a;
 else
 first = a;
 if (first == null)
 tail = p;
 else {
 p.after = first;
 first.before = p;
 }
 head = p;
 ++modCount;
 }
 }
 
 | 
remove方法
删除指定key的内容,和HashMap的remove操作是同一个方法,只是重写了afterNodeRemove方法,将删除节点的前后节点关联起来
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | void afterNodeRemoval(Node<K,V> e) { 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;
 }
 
 | 
基于LinkedHashMap实现的简单的LRU算法
这里我们实现一个简答的LRU算法,当LinkedHashMap中元素大于5时旧将头部的元素删除。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | public class LinkedHashMapTest<K, V> extends LinkedHashMap<K, V>{private static final int THRESHOLD = 5;
 @Override
 public boolean removeEldestEntry(Map.Entry<K,V> eldest) {
 return size() > THRESHOLD;
 }
 
 public static void main(String[] args) {
 LinkedHashMapTest<String, String> linkedHashMap = new LinkedHashMapTest<>();
 for (int i = 0; i < 10; i++) {
 linkedHashMap.put("key:" + i, "value:" + i);
 }
 System.out.println(linkedHashMap);
 }
 }
 执行结果:{key:5=value:5, key:6=value:6, key:7=value:7, key:8=value:8, key:9=value:9}
 
 |