1.概述
- LinkedHashMap继承HashMap,其多种操作都是建立在HashMap操作的基础上。与HashMap不同的是,LinkedHashMap维护了一个Entry的双向链表,保证了插入的Entry的顺序,默认是按照插入顺序,另外一种是访问顺序
2.LinkedHashMap和HashMap的区别
-
HashMap结构图
-
LinkedHashMap结构图
从结构图上看,LinkedHashMap就是在HashMap的基础上,通过双向链表将各个桶中的结点串联起来,这样可以维护元素之间的顺序,而HashMap是不知道结点之间的前后插入顺序的
底层实现数据结构
HashMap
JDK1.8以前:数组+链表
JDK1.8以后:数组+链表+红黑树
LinkedHashMap
在HashMap的基础上再维护了一个双向链表
LinkedHashMap的源码解析
-
从继承关系我们可以看到,LinkedHashMap继承HashMap,并实现了map接口
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {
-
LinkedHashMapEntry继承HashMap的Entry,他表示一个结点(元素);head是头指针,指向第一个结点;tail是尾指针,指向最后一个结点;accessOrder用来控制访问顺序,若为true则采用访问排序,若为false则采用插入排序,默认采用插入排序
/** * 双向链表的头 */ transient LinkedHashMapEntry<K, V> head; /** * 双向链表的尾 */ transient LinkedHashMapEntry<K, V> tail; /** * 一个条件变量,它控制了是否在get操作后需要将新的get的节点重新放到链表的尾部 * LinkedHashMap可以维持了插入的顺序,但是这个顺序不是不变的,可以被get操作打乱。 * * @serial */ final boolean accessOrder; /** * LinkedHashMap的entry包装类 继承了HashMap的Node,也就是在上面添加了前后结点 * * @param <K> * @param <V> */ static class LinkedHashMapEntry<K, V> extends HashMap.Node<K, V> { LinkedHashMapEntry<K, V> before, after; LinkedHashMapEntry(int hash, K key, V value, Node<K, V> next) { super(hash, key, value, next); } }
-
从构造函数来看,并没有做特别操作,只是将accessOrder设置为false,即采用插入排序
//无参数构造函数 默认使用无参数 public LinkedHashMap() { super(); accessOrder = false; } //指定初始容量 public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; } public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); accessOrder = false; } public LinkedHashMap(Map<? extends K, ? extends V> m) { super(); accessOrder = false; putMapEntries(m, false); }
-
LinkedHashMap没有重写put()/putAll()方法,因此其插入方式是和HashMap一样的,只是重写了newNode()/newTreeNode()来构建新结点/树结点,在构建新结点后调用linkNodeLast()将新结点放入双向链表正确的位置,之后再将新创建的结点返回给父类hashMap进行插入操作
//覆盖了父类的newNode方法 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { //生成一个新的结点 LinkedHashMapEntry<K,V> p = new LinkedHashMapEntry<K,V>(hash, key, value, e); //将结点移动 linkNodeLast(p); return p; } // TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) { TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next); linkNodeLast(p); return p; } private void linkNodeLast(LinkedHashMapEntry<K,V> p) { //1.将双向链表tail赋值给临时变量p LinkedHashMapEntry<K,V> last = tail; //将p赋值给尾结点tail tail = p; //若尾结点是空 表示是第一个结点 那么就是头结点 if (last == null) head = p; //若尾结点不为空 则当前结点的前一个结点是之前尾结点,之前尾结点的下一个结点是当前结点 else { p.before = last; last.after = p; } }
-
重写了afterNodeInsertion(),该方法是在插入一个新结点完成之后调用,在这里可以设置是否当缓存满了之后,移除头结点。
void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMapEntry<K,V> first; 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; }
-
LinkedHashMap没有重写remove(),其移除规则还是按照hashMap移除规则,只是重写了afterNodeRemoval(),在方法中的会判断删除的结点有没有前结点,若没有则表示是头结点,那么将删除结点的下一个结点作为头结点,且设置它的前一个结点为null;若不是头结点则再判断当前结点是否为尾结点,那么将删除结点的前一个结点作为尾结点,且设置它的下一个结点为null;若既不是头结点也不是尾结点, 那就是中间结点,那么让删除结点前一个结点的after指向删除结点的后一个结点,让删除结点后的下一个结点的before只想删除结点的前一个结点
/** * 若移除指定结点,判断当前结点不是不是 * @param e */ void afterNodeRemoval(Node<K,V> e) { // unlink //获取移除结点的前后结点 LinkedHashMapEntry<K,V> p = (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after; //将当前结点的前后结点置空 p.before = p.after = null; //若当前结点的前结点为空 表示当前结点是头结点 那么将当前结点的下一个结点变成头结点 if (b == null) head = a; //若当前结点的前结点不为空 表示当前结点是中间的一个结点, //那么当前结点的前一个结点的after是直接指向当前结点的下一个结点的,那么当前结点将会被移除到整个链表中 else b.after = a; //若当前结点的尾结点是空 表示当前结点是最后个结点,那么尾结点就直接是当前结点的前一个结点 if (a == null) tail = b; //当前结点的下一个结点的前结点是指向当前结点的前面结点的 else a.before = b; }
-
重写清除方法,将头尾结点置空
/** * 头尾结点置空 */ public void clear() { super.clear(); head = tail = null; }
-
通过Set遍历获取所有结点的key值,其主要是通过iterator来实现,不断遍历结点,然后取其key值
public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new LinkedKeySet(); keySet = ks; } return ks; } final class LinkedKeySet extends AbstractSet<K> { public final int size() { return size; } public final void clear() { LinkedHashMap.this.clear(); } public final Iterator<K> iterator() { return new LinkedKeyIterator(); } public final boolean contains(Object o) { return containsKey(o); } public final boolean remove(Object key) { return removeNode(hash(key), key, null, false, true) != null; } public final Spliterator<K> spliterator() { return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED | Spliterator.DISTINCT); } public final void forEach(Consumer<? super K> action) { if (action == null) throw new NullPointerException(); int mc = modCount; // Android-changed: Detect changes to modCount early. for (LinkedHashMapEntry<K,V> e = head; (e != null && modCount == mc); e = e.after) action.accept(e.key); if (modCount != mc) throw new ConcurrentModificationException(); } } final class LinkedKeyIterator extends LinkedHashIterator implements Iterator<K> { public final K next() { return nextNode().getKey(); } } abstract class LinkedHashIterator { LinkedHashMapEntry<K,V> next; LinkedHashMapEntry<K,V> current; int expectedModCount; LinkedHashIterator() { next = head; expectedModCount = modCount; current = null; } public final boolean hasNext() { return next != null; } final LinkedHashMapEntry<K,V> nextNode() { LinkedHashMapEntry<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e; next = e.after; return e; } public final void remove() { Node<K,V> p = current; if (p == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); current = null; K key = p.key; removeNode(hash(key), key, null, false, false); expectedModCount = modCount; } }
-
通过Collection遍历获取所有结点的value值,其主要是通过iterator来实现,不断遍历结点,然后取其value值
public Collection<V> values() { Collection<V> vs = values; if (vs == null) { vs = new LinkedValues(); values = vs; } return vs; } final class LinkedValues extends AbstractCollection<V> { public final int size() { return size; } public final void clear() { LinkedHashMap.this.clear(); } public final Iterator<V> iterator() { return new LinkedValueIterator(); } public final boolean contains(Object o) { return containsValue(o); } public final Spliterator<V> spliterator() { return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED); } public final void forEach(Consumer<? super V> action) { if (action == null) throw new NullPointerException(); int mc = modCount; // Android-changed: Detect changes to modCount early. for (LinkedHashMapEntry<K,V> e = head; (e != null && modCount == mc); e = e.after) action.accept(e.value); if (modCount != mc) throw new ConcurrentModificationException(); } } final class LinkedValueIterator extends LinkedHashIterator implements Iterator<V> { public final V next() { return nextNode().value; } } abstract class LinkedHashIterator { LinkedHashMapEntry<K,V> next; LinkedHashMapEntry<K,V> current; int expectedModCount; LinkedHashIterator() { next = head; expectedModCount = modCount; current = null; } public final boolean hasNext() { return next != null; } final LinkedHashMapEntry<K,V> nextNode() { LinkedHashMapEntry<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e; next = e.after; return e; } public final void remove() { Node<K,V> p = current; if (p == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); current = null; K key = p.key; removeNode(hash(key), key, null, false, false); expectedModCount = modCount; } }
-
通过Collection遍历获取所有结点,其主要是通过iterator来实现
public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es; } final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> { public final int size() { return size; } public final void clear() { LinkedHashMap.this.clear(); } public final Iterator<Map.Entry<K,V>> iterator() { return new LinkedEntryIterator(); } public final boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>) o; Object key = e.getKey(); Node<K,V> candidate = getNode(hash(key), key); return candidate != null && candidate.equals(e); } public final boolean remove(Object o) { if (o instanceof Map.Entry) { Map.Entry<?,?> e = (Map.Entry<?,?>) o; Object key = e.getKey(); Object value = e.getValue(); return removeNode(hash(key), key, value, true, true) != null; } return false; } public final Spliterator<Map.Entry<K,V>> spliterator() { return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED | Spliterator.DISTINCT); } public final void forEach(Consumer<? super Map.Entry<K,V>> action) { if (action == null) throw new NullPointerException(); int mc = modCount; // Android-changed: Detect changes to modCount early. for (LinkedHashMapEntry<K,V> e = head; (e != null && mc == modCount); e = e.after) action.accept(e); if (modCount != mc) throw new ConcurrentModificationException(); } } // Map overrides public void forEach(BiConsumer<? super K, ? super V> action) { if (action == null) throw new NullPointerException(); int mc = modCount; // Android-changed: Detect changes to modCount early. for (LinkedHashMapEntry<K,V> e = head; modCount == mc && e != null; e = e.after) action.accept(e.key, e.value); if (modCount != mc) throw new ConcurrentModificationException(); } public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { if (function == null) throw new NullPointerException(); int mc = modCount; // Android-changed: Detect changes to modCount early. for (LinkedHashMapEntry<K,V> e = head; modCount == mc && e != null; e = e.after) e.value = function.apply(e.key, e.value); if (modCount != mc) throw new ConcurrentModificationException(); } final class LinkedEntryIterator extends LinkedHashIterator implements Iterator<Map.Entry<K,V>> { public final Map.Entry<K,V> next() { return nextNode(); } } abstract class LinkedHashIterator { LinkedHashMapEntry<K,V> next; LinkedHashMapEntry<K,V> current; int expectedModCount; LinkedHashIterator() { next = head; expectedModCount = modCount; current = null; } public final boolean hasNext() { return next != null; } final LinkedHashMapEntry<K,V> nextNode() { LinkedHashMapEntry<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e; next = e.after; return e; } public final void remove() { Node<K,V> p = current; if (p == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); current = null; K key = p.key; removeNode(hash(key), key, null, false, false); expectedModCount = modCount; } }
-
通过getNode()获取结点,若获取到结点后判断是否需要进行访问排序
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) { // move node to last LinkedHashMapEntry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMapEntry<K,V> p = (LinkedHashMapEntry<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; } }
总结
- LinkedHashMap就是在HashMap基础上维护了一个双向链表,它将插入的结点的前后连接起来,可以进行排序操作
- LinkedHashMap本身并不实现插入操作,只是重写了创建新结点/树结点的方法,在创建结点后先插入到双向链表,之后返回给HashMap进行插入操作,最后插入完成之后还可以操作当缓存满了之后删除头结点的操作
- LinkedHashMap本身并不实现删除操作,只是重写了删除之后的方法,在hashMap删除结点之后将结点从双向链表中删除掉
- LinkedHashMap重写了get(),获取到结点之后可以根据条件进行排序操作
- 重写了keySet()/values()/entrySet()遍历双向链表的结点来实现迭代
- 重写clear(),在HashMap清除结点之后,将双向链表清空。