目录
- 一、LinkedHashMap
- 二、排序实现
- 三、代码片段分析
一、LinkedHashMap
LinkedHashMap 是 Java 中的一个集合类,它是 HashMap 的一个子类,继承了 HashMap 的所有特性,并且在此基础上增加了一个双向链表来维护元素的插入顺序或者访问顺序。LinkedHashMap 通常用于需要保持插入顺序或者访问顺序的场合。
以下是 LinkedHashMap 的一些特点:
- 顺序性:可以按照插入顺序或者访问顺序来迭代映射中的元素。
- 性能:与 HashMap 类似,LinkedHashMap 在大多数情况下也提供了常数时间的性能。
- 内存消耗:相比于 HashMap,LinkedHashMap 会消耗更多的内存,因为它需要额外的内存来维护双向链表。
- LinkedHashMap 可以通过构造函数来指定是否按照插入顺序或者访问顺序来迭代:
默认情况下,LinkedHashMap 按照插入顺序来迭代。
如果在构造函数中传入 accessOrder 参数为 true,则按照访问顺序来迭代。
二、排序实现
LinkedHashMap继承自
HashMap并添加了一个双向链表来维护元素的插入顺序或访问顺序。以下是
LinkedHashMap` 实现排序的原理和方法:
-
插入顺序排序:LinkedHashMap 默认按照元素的插入顺序进行排序。当元素被插入到 LinkedHashMap中时,它们会按照插入的顺序排列在双向链表中。即使后续元素的值被更新,它们在链表中的位置也不会改变,除非手动调用
remove
方法或者通过迭代器显式地删除元素。 -
访问顺序排序:可以通过在创建 LinkedHashMap实例时设置
accessOrder
参数为true
来启用访问顺序排序。在这种模式下,每次通过get
方法访问一个元素时,该元素会被移动到双向链表的末尾,从而保证最近访问的元素在链表的末尾。 -
按值排序:如果需要按照值对 LinkedHashMap 进行排序,可以使用
Collections.sort
方法或者 Java 8 引入的 Stream API。例如,可以使用entrySet().stream().sorted(Map.Entry.comparingByValue())
来对元素进行排序,然后通过forEachOrdered
或collect
方法将排序后的元素放入一个新的LinkedHashMap
中。 -
自定义排序:如果 LinkedHashMap中的值不实现
Comparable
接口,可以通过提供一个自定义的Comparator
来实现排序。例如,使用 Map.Entry.comparingByValue(Comparator.comparing(Player::getScore)) 来对包含自定义对象的 LinkedHashMap`进行排序。
三、代码片段分析
LinkedHashMap
按照元素的插入顺序进行排序的代码实现主要依赖于其内部的双向链表结构。以下是一些关键的代码片段,展示了 LinkedHashMap
如何维护插入顺序:
-
内部类 Entry:
LinkedHashMap
定义了一个内部类Entry
,继承自HashMap.Node
,并添加了两个新的引用before
和after
用于维护双向链表。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); } }
-
构造方法:
LinkedHashMap
提供了一个构造方法,允许设置accessOrder
参数,默认为false
,表示按照插入顺序排序。public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
-
插入操作:在
put
方法中,LinkedHashMap
调用newNode
方法创建新的Entry
,并通过linkNodeLast
方法将其添加到双向链表的末尾。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; }
-
维护链表:
linkNodeLast
方法将新节点添加到双向链表的末尾,并更新head
和tail
指针。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
重写了get
方法,在返回值之前,如果accessOrder
为false
(即按照插入顺序),则不需要调整链表。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; }
-
迭代器:
LinkedHashMap
的迭代器会根据双向链表的顺序遍历元素,从而保证了迭代顺序与插入顺序相同。
通过这些代码实现,LinkedHashMap能够按照元素的插入顺序进行排序。当 accessOrder
设置为 true
时,LinkedHashMap会转变为按照访问顺序排序,这通常是通过 afterNodeAccess
方法实现的,该方法会在访问元素时将其移动到链表的末尾。