From 60ac0fc12dcd1af819f9695b5aec67f668b8dd2e Mon Sep 17 00:00:00 2001 From: AmyliaY <471816751@qq.com> Date: Sun, 5 Jul 2020 19:59:12 +0800 Subject: [PATCH] =?UTF-8?q?LinkedHashMap=E5=AE=B9=E5=99=A8=E7=B1=BB=20?= =?UTF-8?q?=E6=BA=90=E7=A0=81=E8=B5=8F=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/JDK/collection/LinkedHashMap.md | 258 ++++++++++++++++++++++++++- 1 file changed, 257 insertions(+), 1 deletion(-) diff --git a/docs/JDK/collection/LinkedHashMap.md b/docs/JDK/collection/LinkedHashMap.md index fcb5dbe..7b24c88 100644 --- a/docs/JDK/collection/LinkedHashMap.md +++ b/docs/JDK/collection/LinkedHashMap.md @@ -1 +1,257 @@ -努力编写中... \ No newline at end of file +HashMap 大家都清楚,底层是 数组 + (红黑树 / 链表),**元素是无序的**,而 LinkedHashMap 则比 HashMap 多了这一个功能,并且,LinkedHashMap 的有序可以按两种顺序排列,一种是按照插入的顺序,一种是按照访问的顺序(初始化LinkedHashMap对象时设置accessOrder参数为true),而其内部是靠 建立一个双向链表 来维护这个顺序的,在每次插入、删除后,都会调用一个函数来进行 双向链表的维护,这也是实现 LRU Cache 功能的基础。 + +先说几个比较重要的结论,大家可以根据这些结论从后面的源码解析中 得到证据。 +1. LinkedHashMap 继承了 HashMap,所以和 HashMap 的底层数据结构是一样的,都是数组+链表+红黑树,扩容机制也一样; +2. LinkedHashMap 通过双向链表来维护数据的顺序; +3. LinkedHashMap 存储顺序与添加顺序默认是一样得,同时可以根据 accessOrder参数 来决定是否在访问时移动元素,以实现 LRU 功能。 + +```java +public class LinkedHashMap extends HashMap implements Map { + + /** + * 在 HashMap.Node节点 的基础上增加了 “前继节点” 和 “后继节点” 这种双向链表的功能特性 + */ + static class Entry extends HashMap.Node { + Entry before, after; + Entry(int hash, K key, V value, Node next) { + super(hash, key, value, next); + } + } + + /** + * 记录这个 LinkedHashMap容器的 头节点 + */ + transient LinkedHashMap.Entry head; + + /** + * 记录这个 LinkedHashMap容器的 尾节点 + */ + transient LinkedHashMap.Entry tail; + + /** + * 是否根据访问 进行排序,true为是,可通过构造方法进行设置 + */ + final boolean accessOrder; + + // 下面是一些私有的内部公用方法 + + // 将元素连接到链表尾部 + private void linkNodeLast(LinkedHashMap.Entry p) { + LinkedHashMap.Entry last = tail; + tail = p; + if (last == null) + head = p; + else { + p.before = last; + last.after = p; + } + } + + // apply src's links to dst + private void transferLinks(LinkedHashMap.Entry src, LinkedHashMap.Entry dst) { + LinkedHashMap.Entry b = dst.before = src.before; + LinkedHashMap.Entry a = dst.after = src.after; + if (b == null) + head = dst; + else + b.after = dst; + if (a == null) + tail = dst; + else + a.before = dst; + } + + // 下面是一些 重写的 HashMap 的 hook methods,其中 afterNodeInsertion、afterNodeRemoval + // afterNodeAccess及方法,在每次插入、删除、访问后,都会回调 用来维护双向链表 + + void reinitialize() { + super.reinitialize(); + head = tail = null; + } + + Node newNode(int hash, K key, V value, Node e) { + LinkedHashMap.Entry p = + new LinkedHashMap.Entry(hash, key, value, e); + linkNodeLast(p); + return p; + } + + Node replacementNode(Node p, Node next) { + LinkedHashMap.Entry q = (LinkedHashMap.Entry)p; + LinkedHashMap.Entry t = + new LinkedHashMap.Entry(q.hash, q.key, q.value, next); + transferLinks(q, t); + return t; + } + + TreeNode newTreeNode(int hash, K key, V value, Node next) { + TreeNode p = new TreeNode(hash, key, value, next); + linkNodeLast(p); + return p; + } + + TreeNode replacementTreeNode(Node p, Node next) { + LinkedHashMap.Entry q = (LinkedHashMap.Entry)p; + TreeNode t = new TreeNode(q.hash, q.key, q.value, next); + transferLinks(q, t); + return t; + } + + // 在删除元素之后,将元素从双向链表中删除 + void afterNodeRemoval(Node e) { // unlink + LinkedHashMap.Entry p = + (LinkedHashMap.Entry)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; + } + + // 可用于删除最老的元素 + void afterNodeInsertion(boolean evict) { // possibly remove eldest + LinkedHashMap.Entry first; + if (evict && (first = head) != null && removeEldestEntry(first)) { + K key = first.key; + removeNode(hash(key), key, null, false, true); + } + } + + // 在访问元素之后,将该元素放到双向链表的尾巴处 + void afterNodeAccess(Node e) { // move node to last + LinkedHashMap.Entry last; + if (accessOrder && (last = tail) != e) { + LinkedHashMap.Entry p = + (LinkedHashMap.Entry)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; + } + } + + void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException { + for (LinkedHashMap.Entry e = head; e != null; e = e.after) { + s.writeObject(e.key); + s.writeObject(e.value); + } + } + + /** + * 跟 HashMap 的构造方法没啥区别,初始容量、扩容因子 用以减少resize和rehash,提升容器整体性能 + */ + public LinkedHashMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + accessOrder = false; + } + + public LinkedHashMap(int initialCapacity) { + super(initialCapacity); + accessOrder = false; + } + + /** + * 注意!accessOrder参数默认为false,如果想使用 LRU机制,记得设为 true + */ + public LinkedHashMap() { + super(); + accessOrder = false; + } + + public LinkedHashMap(Map m) { + super(); + accessOrder = false; + putMapEntries(m, false); + } + + /** + * 使用这个构造方法 设置accessOrder + */ + public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { + super(initialCapacity, loadFactor); + this.accessOrder = accessOrder; + } + + /** + * 是否包含指定元素 + */ + public boolean containsValue(Object value) { + for (LinkedHashMap.Entry e = head; e != null; e = e.after) { + V v = e.value; + if (v == value || (value != null && value.equals(v))) + return true; + } + return false; + } + + /** + * 获取指定key对应的value,如果accessOrder为true,会回调afterNodeAccess方法 + * 将元素放到队尾 + */ + public V get(Object key) { + Node e; + if ((e = getNode(hash(key), key)) == null) + return null; + if (accessOrder) + afterNodeAccess(e); + return e.value; + } + + /** + * {@inheritDoc} + */ + public V getOrDefault(Object key, V defaultValue) { + Node e; + if ((e = getNode(hash(key), key)) == null) + return defaultValue; + if (accessOrder) + afterNodeAccess(e); + return e.value; + } + + /** + * {@inheritDoc} + */ + public void clear() { + super.clear(); + head = tail = null; + } + + /** + * 获取key的set集合 + */ + public Set keySet() { + Set ks = keySet; + if (ks == null) { + ks = new LinkedKeySet(); + keySet = ks; + } + return ks; + } + + /** + * 返回 键值对 的Set集合 + */ + public Set> entrySet() { + Set> es; + return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es; + } +} +``` \ No newline at end of file