前言
LinkedHashMap继承于HshMap,维护了一条双向链表,解决了遍历顺序和插入顺序问题。
原理
LinkedHashMap继承于HashMap,所以底层还是数组+链表)+红黑树结构。每次的数据插入操作,都会附带上前后指针,形成一条有序链路。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {...}
LinkedHashMap
相比较于HashMap对应的多了以下参数。
/*** The head (eldest) of the doubly linked list. 头结点*/transient LinkedHashMap.Entry<K,V> head;/*** The tail (youngest) of the doubly linked list. 尾结点*/transient LinkedHashMap.Entry<K,V> tail;/*** The iteration ordering method for this linked hash map: <tt>true</tt>* for access-order, <tt>false</tt> for insertion-order.* true:访问顺序;false:插入顺序*/final boolean accessOrder;
Entry
带有前后节点
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);}}
Put
put还是复用的HashMap方法,但是重写了newNode方法。每次插入的新节点,都带有before和after的前后引用。
//HashMappublic V put(K key, V value) {return putVal(hash(key), key, value, false, true);}//HashMapfinal 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)//overridetab[i] = newNode(hash, key, value, null);else {...}//LinkedHashMapNode<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;}// link at the end of listprivate void linkNodeLast(LinkedHashMap.Entry<K,V> p) {LinkedHashMap.Entry<K,V> last = tail;//设置tail节点是新元素tail = p;if (last == null)//tail节点为空时,头结点也为空,设置下head节点head = p;else {//存在尾结点时,进行设置前后节点p.before = last;last.after = p;}}
Remove
删除方法也是复用的HashMap的,数据的删除都是和HashMap一样的,最后删除完了后,修改下节点的引用关系。
//HashMappublic V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;}final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {...if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)tab[index] = node.next;elsep.next = node.next;++modCount;--size;//overrideafterNodeRemoval(node);return node;}}//LinkedHashMapvoid afterNodeRemoval(Node<K,V> e) { // unlinkLinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;//删除节点before,after置为空p.before = p.after = null;//头结点处理if (b == null)head = a;elseb.after = a;//尾结点处理if (a == null)tail = b;elsea.before = b;}
Get
LinkedHashMap默认是按照插入顺序排序的,指定accessOrder=true,就按照访问顺序。方法还是复用的HashMap方法,调用get/getOrDefault/replace时,将节点移动到链表尾部。
//LinedHashMappublic 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 lastLinkedHashMap.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//after节点修改下一指向b.after = a;if (a != null)//before节点修改前一指向a.before = b;elselast = b;if (last == null)head = p;else {p.before = last;last.after = p;}//设置为尾部节点tail = p;++modCount;}}
缓存
当LinkedHashMap操作任何put方法后,都会触发一个字方法回调,这里可以做额外的处理。
void afterNodeInsertion(boolean evict) { // possibly remove eldestLinkedHashMap.Entry<K,V> first;//evict 是否删除;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;}
有了这个,可以实现removeEldestEntry 方法,当put元素时,会根据覆盖的方法来控制长度。
