首先,查看LinkedHashMap的注释,了解到它主要有以下几点需要注意,后面将分别贴出对应代码,进行理解
一、LinkedHashMap的几点特性
- 持有一个双向链表,与hashMap和hashTable不同,它可以保持插入顺序。相比treeMap,它可以拥有更高的性能(后者留待后面研究treeMap之后再填坑)。
- 它很适合创建LRU队列。原因:有一个比较特殊的构造函数(LinkedHashMap(int,float,boolean)) ,可使按照最近访问的顺序进行迭代
- collection-views是什么,access-ordered 与insertion-ordered是什么
- 它相对hashmap,因为要维护双向链表可能会稍微多点性能开销,但有一个例外:linkedHashmap对collection-view的迭代时间开销与size有关,与capacity无关,hashmap则与capacity也有关(此处如何理解?)
- 两个参数会影响性能:初始容量 与 负载因子
- 非线程安全
- 关于插入顺序,对于insertion-ordered,如果插入的key已经存在,而仅仅只 改变key的值,则不会改变插入顺序。对于access-ordered,使用get查询是一个结构性修改
- 任何未通过迭代器进行的结构性调整都会导致ConcurrentModificationException
二、代码理解
接下来,先上它的底层存储结构,比较与hashMap的不同:
// 是hashmap的子类
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
// 双向链表的头
transient LinkedHashMap.Entry<K,V> head;
// 双向链表的尾
transient LinkedHashMap.Entry<K,V> tail;
// 当为true时,表示迭代器会按访问顺序遍历,false则按插入顺序遍历。默认值是false
final boolean accessOrder;
LinkedHashMap 的主要特性在于根据accessOrder,维持遍历顺序,
// 在hashmap中调用put方法时,会执行此方法,主要做的操作是判断是否要移除第一个节点
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
// 注意:当linkedhashmap第一次添加元素时,head并不是null,而是“C:\Program Files\Java\jdk1.8.0_271\jre\bin\”下面的节点,为何这样设计?
// 另外removeEldestEntry涉及到io下的ExpiringCache,根据当前map的size是否超过MAX_ENTRIES(200),决定是否执行removeNode方法
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
// 此处调用的还是父类hashmap的removeNode
removeNode(hash(key), key, null, false, true);
}
}
// 此方法做的操作是,当处于访问顺序模式,将进行了访问的节点提到链表的末尾
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, prev = p.before, next= p.after;
// 代码省略
.....
}
}
三、小结
接下来解答以下特点(部分知识点暂时欠缺,留坑)
- linkedHashMap如何保持插入顺序?
// 在hashMap中已经预留了此方法,并由linkedHashMap进行覆盖,插入节点时,进行链表操作
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;
}
}
2.linkedHashmap的迭代为何与容量无关?因为linkedHashmap迭代的是链表,只与数据有关。而hashmap是遍历底层数组,所以与容量有关。
3.access-ordered 与insertion-ordered是在创建linkedHashMap的时候确定的,表示链表的访问顺序,一旦确立无法改变