LinkedHashMap简介
LinkedHashMap是HashMap的子类,基于HashMap的数据结构,维护了一个双向链表。
所以相对HashMap来说,LinkedHashMap可以保证取数的顺序。
类定义如下
1 2 3
| public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
|
基础属性
1 2 3 4 5 6 7 8 9 10 11 12
| 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); } }
transient LinkedHashMap.Entry<K,V> head; transient LinkedHashMap.Entry<K,V> tail;
final boolean accessOrder;
|
put操作
LinkedHashMap继承了HashMap,他的put方法没有重写,直接使用的HashMap的put方法。
但是,LinkedHashMap需要维护一个链表结构,所以在重写newNode()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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; } }
|
LinkedHashMap的put操作逻辑与HashMap完全一样,只是在新建节点时通过重写方法,增加了链表维护的逻辑。
get操作
get操作与put操作不同,LinkedHashMap直接重写了父类的方法,并在其中增加了取数顺序的判断及处理逻辑。
1 2 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
| 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) { 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; } }
|
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public void linkedHashMapTest() { LinkedHashMap<Integer, Integer> linkedHashMap = new LinkedHashMap<>(); Set<Integer> set = linkedHashMap.keySet(); linkedHashMap.put(1, 1); linkedHashMap.put(2, 2); linkedHashMap.put(22, 22); for (Integer key : set) { logger.info("key:{}", key); } linkedHashMap.get(2); for (Integer key1 : set) { logger.info("key1:{}", key); } }
|
结果输出:
get方法并不会影响取值的顺序,永远按照插入顺序取出数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public void linkedHashMapTest() { LinkedHashMap<Integer, Integer> linkedHashMap1 = new LinkedHashMap<>(16, (float) 0.75, true); linkedHashMap.put(1, 1); linkedHashMap.put(2, 2); linkedHashMap.put(22, 22); for (Integer key : set) { logger.info("key1:{}", key); } linkedHashMap1.get(2); for (Integer key : set) { logger.info("key2:{}", key); } linkedHashMap1.get(22); for (Integer key : set) { logger.info("key3:{}", key); } }
|
结果输出:
当通过构造方法将accessOrder设置为true时,get方法除了查到节点,还会将所查到的节点放入到链表队尾,实现访问顺序的维护。
总结
LinkedHashMap大部分内容都与HashMap一样,其通过重写部分方法,在HashMap的操作逻辑基础上,穿插了
链表的操作逻辑。
另外,LinkedHashMap还提供了访问顺序的维护,逻辑很简单,每一次get取数时,就将这个数据所属节点放
到链表队尾,这样遍历数据时,就是按照最近最少使用的顺序输出。也是一种LRU算法的实现思路。