Java 数据结构 -- 16.Java 8 数据结构 LinkedHashMap

前言

书接上文,上一篇中对 Map 接口的 最终实现类 HashMap 进行了介绍与分析,本篇将对 HashMap 的子类,实现了恒定顺序特性的 LinkedHashMap 进行介绍与分析。

这里就不再放图了,直接来看 LinkedHashMap 的源码

/**
 * Map 接口的 Hash table 和链接链表实现,伴随一个可预测的迭代操作顺序(恒等顺序)。这个实现与 HashMap 不
 * 同的地方在于维护了一个覆盖它的所有条目的双向链表。这个链接链表定义了迭代操作的顺序,通常是键s加入 map 的顺
 * 序(iteration-order)。注意迭代操作顺序不会手动重新加入 map 的键s的影响。(如果对于键 k,map m 在 
 * m.containsKey(k) 返回是 true 的时候马上调用 m.put(k, v) 那么 k 就是重新加入的。)
 *
 * 这个实现类省去了它的客户端从一个 {@link HashMap} (and {@link Hashtable}) 中不确定的,通常是混乱的顺
 * 序的打扰,而不会带来增加的有关 {@link TreeMap} 的性能消耗。他可以被用来产生一个与原始的,不用考虑原始 
 * map 的实现的,比如
 * 
 *     void foo(Map m) {
 *         Map copy = new LinkedHashMap(m);
 *         ...
 *     }
 * 
* 的保持相同顺序的拷贝。 * * 这个技术主要在一个以 map 为输入的模块,拷贝它,然后在稍后的时间返回一个顺序被拷贝对象定义的拷贝时有用。 * (客户端通常对于它们提供的有相同顺序的东西心怀感激。) * * 一个特殊的 {@link #LinkedHashMap(int,float,boolean) constructor} 被提供来建立一个迭代操作顺序与 * 最终访问顺序(access-order)相同的链接 hash map。这种 map 很适合建立 LRU 缓存。调用 {@code put}, * {@code putIfAbsent}, {@code get}, {@code getOrDefault}, {@code compute}, {@code * computeIfAbsent}, {@code computeIfPresent}, or {@code merge} 方法返回一个对应条目的访问(加入它 * 在调用结束后存在)。{@code replace} 方法只在一个条目的值被替换时候返回访问。{@code putAll} 方法生为指 * 定 map 中的每一个映射生成一个条目访问,以指定 map 的 entry set 迭代器提供的键值映射的顺序。没有其他方法 * 生成条目访问。特别要指出的是,对于 collection 视图的操作不会影响提供支持的 map 的迭代操作。 * * {@link #removeEldestEntry(Map.Entry)} 方法可以被重写来强制执行一个策略,在新的映射加入到 map 中时 * 自动地移除过期的映射。 * * 这个类提供所有可选的 Map 操作,并且允许 null 元素。就像 HashMap,它对于基础操作(add,contains 和 * remove)提供一个时间恒定的性能,假设 hash 功能很好的将元素分散到了各个桶中。性能看上去只比 HashMap 低一 * 点,因为要加入维护链接链表的成本,伴随一个异常:对于一个 LinkedHashMap 的 collection 视图的迭代操作需 * 要与 map 长度成比例的时间,不用考虑它的容量。涵盖一个 HashMap 的迭代操作看上去会更昂贵,需要与容量成比例 * 的时间。 * * 一个链接 hash map 有两个要素会影响它的性能: * initial capacity(初始化容量)和 load factor(加载因子)。它们就像 HashMap 那样被精确地定义。注意, * 但是,选择过高的初始化容量值的惩罚对比 HashMap,这个类没有这么严重,因为对于这个类的迭代操作时间是不受容 * 量影响的。 * * 注意这个实现类不是线程安全的。如果多个线程同事访问一个 linked hash map,并且至少一个线程结构性地修改了 * map,它必须从外部实现线程安全。这通常是通过在一些对象中自然封装这个 map 来完成的。 * * 如果这样的对象不存在,map 应该使用 * {@link Collections#synchronizedMap Collections.synchronizedMap} * 方法来“封装”。这最好在创建的时候做,来避免对于这个 map 意外的非线程安全的访问:
 *   Map m = Collections.synchronizedMap(new LinkedHashMap(...));
* * 一个结构化的修改是任何添加或者删除一个或多个映射操作,在 access-ordered linked hash maps 的情况中, * 会影响迭代操作的顺序。在 insertion-ordered linked hash maps 中,仅仅改变 map 中已经存在键的值不是一 * 个结构性改变。在 access-ordered linked hash maps 中,仅仅通过 get 查询 map 不是一个结构性改变。 * * 所有这个类的 colleciton 视图返回的 collections 的 iterator 方法返回的迭代器都是 fail-fast 的:如果 * 在迭代操作创建后的任何时间点这个 map 被结构性的更改了,以除了通过迭代器自己的 remove 方法之外的任何方 * 式。迭代器将抛出一个 {@link ConcurrentModificationException}。因此,对于并发修改,迭代器失败的快速 * 与干净,而不是在未来某个不确定的时间点冒险做任何不确定的行为。 * * 注意一个迭代器的 fail-fast 行为不能提供像它所描述的那样的保证,一般来说,不可能对于不同步的并发操作做任何 * 硬性的保证。基于最好性能的基础,fail-fast 迭代器抛出一个 ConcurrentModificationException。因此,编 * 写一个依赖于这个异常来保证其正确性的程序是错误的:迭代器的 fail-fast 行为应该只被用于检查 bugs。 * * 通过这个类的 collections 视图返回的 coillection 的 spliterator 方法返回的并行迭代器是延迟绑定, * fail-fast,和另外记录的{@link Spliterator#ORDERED}。 * * LinkedHashMap 继承自 HashMap,实现了 Map 接口。 * * 注意本类中全程看不到 table 与 tab,但是它还是 HashMap 散列表特性的,说明它直接使用了 HashMap 的底层结 * 构,而没有改动,也就是数组结构(桶,可能是一个 Node,可能是一个 Node 链表,也可能是一颗红黑 Node 树), * 所谓的 Linked 链接的就是忽略了桶的种类,将所有的条目双向链接在一起而已。 * * 和 HashMap 相同的部分已经在 HashMap 篇中做过介绍,这里只翻译与解释不同的部分。 */
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> { /* * 实现类注意。这个类与它的前置版本在内部结构上有一点小差异。因为父类 HashMap 现在对于一些它的 nodes * 使用 tress,LinkedHashMap.Entry 类现在被当做中间 node 类对待使它也可以转换成 tree 的形式。这个 * 类的名字,LinkedHashMap.Entry,在它当前的上下文中在多个方面都费解的,但是不能被改变了。不然的话, * 即使他没有在当前包外部被传播,一些已知的依赖代码会发生错误(这一段太长了不是很好翻译,就简写了)。所 * 以,我们保持名称来保存未修改的可编译性。 * * node 类s的改变也需要使用到两个类属性(head,tail)而不是一个指向 header node 的指针来维护双向链表 * before/after 链表。这个类先前也是在访问,插入和移除时使用使用不同风格的回调方法。 /** * 面向普通 LinkedHashMap 条目s 的 HashMap.Node 子类,继承自 HashMap.Node。 */ static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; //新增了 before 与 after 属性,注意这里的 after 与 next 是两个属性,为了与 next(后节点区分开),一下对于 before 与 after 的称呼为前条目和后条目,head 与 tail 的称呼为头条目与尾条目 Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } } private static final long serialVersionUID = 3801124242820219131L; /** * 双向链接链表的头(最老旧的)条目 */ transient LinkedHashMap.Entry<K,V> head; /** * 双向链接链表的尾(最年轻的)条目 */ transient LinkedHashMap.Entry<K,V> tail; /** * 当前 linked hash map 的迭代操作排序方法: * 对于 access-order true * 对于 insertion-order false */ final boolean accessOrder; // 内部工具 // 链接在链表尾部 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) //如果当前 LinkedHashMap 的尾条目为 null head = p; //p 赋值给头条目 else { //如果当前 LinkedHashMap 的尾条目不为 null p.before = last; //链接参数条目与原来的尾条目 last.after = p; //链接原来的尾巴条目与参数条目 } } // 应用 src 的链接s到 dst private void transferLinks(LinkedHashMap.Entry<K,V> src, LinkedHashMap.Entry<K,V> dst) { LinkedHashMap.Entry<K,V> b = dst.before = src.before; //获取 src 的前条目关系,更新给 dst LinkedHashMap.Entry<K,V> a = dst.after = src.after; //获取 src 的后条目关系,更新给 dst if (b == null)//如果前条目为 null head = dst; //dst 就是头条目 else //如果前条目不为 null b.after = dst; //更新 b 的链接关系,将 dsc 链接到 src 条目前条目的后面 if (a == null) //如果后条目为 null tail = dst; //dst 就是尾条目 else //如果后条目不为 null a.before = dst; //更新 a 的链接关系,将 dst 连接到 src 条目后条目的前面 } // 重写 HashMap 钩子方法 //重新初始化方法 void reinitialize() { super.reinitialize(); //调用 HashMap 的 reinitialize 方法 head = tail = null; //头条目与尾条目置为 null } //获取新 Node 方法 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); //构造新 LinkedHashMap 条目 linkNodeLast(p); //调用 linkNodeLast 方法 return p; //返回 p } //替换 Node 方法 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; //强转参数 p LinkedHashMap.Entry<K,V> t = new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next); //构造新 LinkedHashMap 条目 transferLinks(q, t); //调用 transferLinks 方法 return t; //返回 t } //获取新的 TreeNode 方法 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); //构造新 TreeNode linkNodeLast(p); //调用 linkNodLast 方法 return p; //返回 p } //替换 TreeNode 方法 TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; //强转参数 p TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next); //构造新 TreeNode transferLinks(q, t); //调用 transferLinks 方法 return t; } //注意一下三个方法对应 HashMap 中提到的回调方法,所以这里不是条目,而是 Node,就是这三个方法保证了 HashMap 的双向链接特性 //移除 Node 后方法 void afterNodeRemoval(Node<K,V> e) { // 断开链接 LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; //强转参数,缓存强转后的 LinkedHashMap 条目,以及前条目和后条目 p.before = p.after = null; //p 的前后节点置为 null //更新链接关系,与 transferLinks 一样 if (b == null) head = a; else b.after = a; if (a == null) tail = b; else a.before = b; } //在 Node 添加后方法 //evict 的含义在 HashMap 篇中有解释,就是是否初始化构造 Map void afterNodeInsertion(boolean evict) { // 存在移除最古老节点的可能性 LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) { //如果是初始化构造 LinkedHashMap 并且头条目不为 null 并且调用 removeEldestEntry 返回 true K key = first.key; //键为头条目的键 removeNode(hash(key), key, null, false, true); //调用 HashMap 的 removeNode 方法 } } //在 Node 访问后方法 void afterNodeAccess(Node<K,V> e) { // 移动 Node 到最后(调整最近访问链) LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { //如果是 accessOrder 并且尾条目不为参数 LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; //强转参数为 LinkedHashMap 条目,同时缓存该条目的前条目和后条目 p.after = null; //后条目置为 null if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; //更新链接关系,前面都与 transferLinks 一样,直到这一步,如果参数条目的后条目为 null,那么将参数前条目赋值给 last if (last == null) //如果 last 为 null,说明参数条目不存在前条目 head = p; //将参数条目赋值给头条目 else { //如果 last 不为 null,说明参数条目前条目存在 p.before = last; //将参数条目的前条目位置 last last.after = p; //last 的后条目置为参数条目(这不是跟之前一样么。。) } tail = p; //尾条目置为参数条目 ++modCount; //结构性变更,注意这里其实长度没有变更,只是变更了链接关系,自增 modCount } } //内部写条目方法,注意对应到 HashMap 的序列化写方法 void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { //从头节点步移 s.writeObject(e.key); //写键 s.writeObject(e.value); //写值 } } /** * 构造一个带有指定初始化容量和加载因子的空的 insertion-ordered LinkedHashMap 实例。 */ public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); //调用 HashMap 的双参构造方法 accessOrder = false; //accessOrder 置为 false } /** * 构造一个带有指定初始化容量和一个默认加载因子(0.75)的空的 insertion-ordered LinkedHashMap 实 * 例。 */ public LinkedHashMap(int initialCapacity) { super(initialCapacity); //调用 HashMap 的单参构造方法 accessOrder = false; //accessOrder 置为 false } /** * 构造一个带有默认初始化容量(16)和默认加载因子(0.75)的一个空的 insertion-ordered * LinkedHashMap 实例。 */ public LinkedHashMap() { super(); //调用 HashMap 的无参构造方法 accessOrder = false; //accessOrder 置为 false } /** * 构造一个与指定参数带有相同映射s的一个 insertion-ordered LinkedHashMap 实例。这个 * LinkedHashMap 实例被用一个默认加载影子(0.75)和一个足够容纳指定 map 中所有映射大小的初始化容量创 * 建。 */ public LinkedHashMap(Map<? extends K, ? extends V> m) { super(); //调用 HashMap 的无参构造方法 accessOrder = false; //accessOrder 置为 false putMapEntries(m, false); //调用 HashMap#putMapEntries 方法 } /** * 构造一个带有指定初始化容量,加载因子和排序模式的空的 LinkedHashMap 实例。 */ public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); //调用 HashMap 的双参构造方法 this.accessOrder = accessOrder; //将 accessOrder 参数赋值给当前 LinkedHashMap 实例的 accessOrder } /** * 如果当前 map 映射到对应指定值的一个或多个键。 */ public boolean containsValue(Object value) { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { //循环 LinkedHashMap 条目 V v = e.value; if (v == value || (value != null && value.equals(v))) return true; //如果返现相同条目,返回 true } return false; //否则返回 false } /** * 返回指定键对应的值,或者如果当前 map 不包含对于这个 key 的映射,返回 {@code null}。 * * 更正式地说,如果 map 包含一个键 {@code k} 到一个值 {@code v} 存在 {@code (key==null ? * k==null : key.equals(k))} 关系,那么这个方法返回 {@code v};不然的话它返回 {@code null}。 * (只可能之多有一个这样的映射。) * * 一个返回值 {@code null} 不能明确地表示当前 map 不包含这个 key 所对应的映射;它也有可能是这个 map * 明确地将键映射到了 {@code null}。{@link #containsKey containsKey} 操作可以被用来区分这两种情 * 况。 */ public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) //调用 HashMap#getNode return null; if (accessOrder) //如果是 accessOrder 的 afterNodeAccess(e); //调用 afterNodeAccess,返回最新访问的 Node return e.value; //返回 e 的值 } /** * 文档继承 */ public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) //调用 HashMap#getNode return defaultValue; //如果返回为 null,返回 defaultValue if (accessOrder) //如果是 accessOrder 的 afterNodeAccess(e); //调用 afterNodeAccess,返回最新访问的 Node return e.value; //返回 e 的值 } /** * 文档继承 */ public void clear() { super.clear(); //调用 HashMap#clear head = tail = null; //头条目与尾条目置为 null } /** * 如果当前 map 应该移除它最老旧的条目则返回 true。这个方法在向当前 map 插入一个新的条目被 put 和 * putAll 调用。它为实现着提供了每次插入新条目时候移除最老旧条目的机会。这在如果 map 代表了一个缓存的时 * 候有用:它允许 map 来通过删除陈腐的条目s来减少内存消费。 * * 例子使用:这中重写将允许 map 增长到 100 个条目然后在每次一个新的条目被添加时删除最老旧的条目,维护一 * 个稳定的 100 个条目的状态。 *
     *     private static final int MAX_ENTRIES = 100;
     *
     *     protected boolean removeEldestEntry(Map.Entry eldest) {
     *        return size() > MAX_ENTRIES;
     *     }
     * 
* *。这个方法通常不一任何方式改动当前 map,而是允许 map 直接通过它的返回值修改它自己。它允许这个方法来直 * 接修改 map,但是如果它这么做了,它必须返回 false(示意这个 map 不应该再尝试任何更多的改动)。在这个 * 方法中改变这个 map 后返回 true 的影响是不可指定的。 * * 这个实现仅仅返回 false(所以当前 map 就像一个普通 map 那样运作 - 最老旧的元素永远不会被移除)。 */
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; //返回 false } /** * 返回键 Set 方法 * * 返回当前 map 包含的键s的一个 {@link Set} 视图。这个 set 是被这个 map 支持的,所有改变这个 map * 会影响到这个 set,反之依然。如果这个 map 在一个涵盖 set 的迭代器操作过程中被改动了(除了通过迭代器 * 自己的 remove 操作),迭代操作的返回值是不可定义的。这个 set 支持元素移除,就是从这个 map 中移除相 * 应映射,通过 Iterator.remove,Set.remove,removeAll,retainAll,和 clear 操作。它不支持 add * 或者 addAll 操作。 * * 它的 {@link Spliterator} 与 {@code HashMap} 比通常支持更快的顺序性能但是更低的并行性能。 */ public Set<K> keySet() { Set<K> ks = keySet; //这就是多态的好处了,KeySet 与 LinkeKeySet 都继承自 AbstractSet 实现了 Set 接口,有判断结果来选择返回哪一种 keySet if (ks == null) { //如果 HashMap 的 keySet 为 null ks = new LinkedKeySet(); //调用 LinkedKeySet 空构造器 keySet = ks; //返回 LinkedKeySet 对象 } return ks; //直接返回 KeySet 对象 } /** * LinkedKeySet 类,继承自 AbstractSet **/ final class LinkedKeySet extends AbstractSet<K> { public final int size() { return size; } public final void clear() { LinkedHashMap.this.clear(); } //这里和 HashMap 不同,调用 LinkedHashMap 的 clear 方法 public final Iterator<K> iterator() { return new LinkedKeyIterator(); //和 HashMap 不同,返回空的 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; //与 HashMap 的完全一样,但是由于是继承自 AbstractSet,所以需要再写一遍 } 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; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) //循环方式与 HashMap 不同 action.accept(e.key); //消费条目的键 if (modCount != mc) throw new ConcurrentModificationException(); } } /** * 返回一个包含当前 map 中所有值的 {@link Collection} 视图。 * * 这个 collection 被这个 map 支持,所以对于这个 map 的修改会影响到这个 collection,反之亦然。如果 * 这个 map 在一个涵盖 collection 的迭代器操作过程中被改动了(除了通过迭代器自己的 remove 操作),迭 * 代操作的返回值是不可定义的。这个 collectin 支持元素移除,就是从这个 map 中移除相应映射,通过 * Iterator.remove,Set.remove,removeAll,retainAll,和 clear 操作。 * * 它的 {@link Spliterator} 与 {@code HashMap} 比通常支持更快的顺序性能但是更低的并行性能。 */ public Collection<V> values() { Collection<V> vs = values; //与 LinkedKeySet 方法的逻辑一样 if (vs == null) { vs = new LinkedValues(); //除了当 HashMap 的 values 不存在的时候调用的是空 LinkedValues 构造器 values = vs; } return vs; } /** * LinkedValues 类,继承自 AbstractCollection。 **/ final class LinkedValues extends AbstractCollection<V> { public final int size() { return size; } public final void clear() { LinkedHashMap.this.clear(); } //注意和 HashMap 的 Values 一样没有 remove() 方法 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; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) action.accept(e.value); //消费条目的值 if (modCount != mc) throw new ConcurrentModificationException(); } } /** * 返回一个包含了当前 map 中所有映射的 {@link Set} 视图。这个 set 是被这个 map 支持的,所有改变这个 * map 会影响到这个 set,反之依然。如果这个 map 在一个涵盖 set 的迭代器操作过程中被改动了(除了通过迭 * 代器自己的 remove 操作),迭代操作的返回值是不可定义的。这个 set 支持元素移除,就是从这个 map 中移 * 除相应映射,通过 Iterator.remove,Set.remove,removeAll,retainAll,和 clear 操作。它不支持 * add 或者 addAll 操作。 */ public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es; //和 HashMap 不同,当 entrySet 为 null 时调用的是空 LinkedEntrySet 构造器 } /** * LinkedEntrySet 类,继承自 AbstractSet 类。 **/ 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) { //与 HashMap 完全一样,但是由于是继承自 AbstractSet 的,所以要再写一遍 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; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) action.accept(e); //消费条目对象 if (modCount != mc) throw new ConcurrentModificationException(); } } // Map 重写 // Map forEach 消费方法 public void forEach(BiConsumer<? super K, ? super V> action) { if (action == null) throw new NullPointerException(); int mc = modCount; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) //循环方式与 HashMap 不同 action.accept(e.key, e.value); //消费键/值(void 方法,所以只是消费) 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; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) //循环方式与 HashMap 不同 e.value = function.apply(e.key, e.value); //对键/值应用函数调用(有返回值的方法,所以可以更新) if (modCount != mc) throw new ConcurrentModificationException(); } // 迭代器 // 可以看到是一种适配器模式 abstract class LinkedHashIterator { LinkedHashMap.Entry<K,V> next; //下一个条目 LinkedHashMap.Entry<K,V> current; //当前条目 int expectedModCount; //防止同时修改异常 //注意这里没有游标概念 LinkedHashIterator() { //空构造器 next = head; //头条目赋值给 next expectedModCount = modCount; //LinkedHashMap 的 modCount 赋值给 expectedModCount current = null; //null 赋值给 current } public final boolean hasNext() { //判断是否存在下一个 return next != null; //判断 next 是否不为 null } final LinkedHashMap.Entry<K,V> nextNode() { //返回下一个条目 LinkedHashMap.Entry<K,V> e = next; //缓存 next 给 e if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e; //将 e 赋值给 current next = e.after; //更新 next 为 e 的后节点 return e; //返回 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); //调用 HashMap removeNode 方法 expectedModCount = modCount; //更新 exprectedModCount } } // LinkedKeyIterator 继承自 LinkedHashIterator,实现 Iterator 接口 final class LinkedKeyIterator extends LinkedHashIterator implements Iterator<K> { public final K next() { return nextNode().getKey(); } //返回下一个条目的键 } // LinkedValueIterator 继承自 LinkedHashIterator,实现 Iterator 接口 final class LinkedValueIterator extends LinkedHashIterator implements Iterator<V> { public final V next() { return nextNode().value; } //返回下一个条目的值 } // LinkedEntryIterator 继承自 LinkedHashIterator,实现 Iterator 接口 final class LinkedEntryIterator extends LinkedHashIterator implements Iterator<Map.Entry<K,V>> { public final Map.Entry<K,V> next() { return nextNode(); } //返回下一个条目 } }

可以看到 LinkedHashMap 还是比较清楚的。它主要是对于 HashMap 中的 条目做了双向链表的实现。做法是通过在构造 LinkedHashMap 对象的时候选择合适的顺序(默认顺序,也就是插入顺序,或者访问顺序,也就是最近修改顺序),并通过实现在 HashMap 中预留的 #afterNodeRemoval(#removeNode),#afterNodeInsertion(对应 #putVal,#computeIfAbsent,#compute,#merge),#afterNodeAccess(#putVal,#replace,#computeIfAbsent,#computeIfPresent,#compute,#merge) 方法(这些方法中会根据顺序的类型来更新链接)来达到调整顺序的目的。
还要注意的是 LinkedHashMap 的迭代器不再是像 HashMap 那样遍历数组,再遍历桶了,而是直接循环链表。

以上就是对于 LinkedHashMap 的介绍与分析,下一篇将对 TreeMap 进行介绍与分析。

你可能感兴趣的:(Java,8,数据结构)