LinkedHashMap的特性
链接: link.
可以按照put的顺序读取存储的元素。
LinkedHashMap结构
LinkedHashMap继承于HashMap,即所有的节点还是存储到HashMap中,但是与HashMap中的Node节点不同的是,LinkedHashMap中的Entry节点继承于HashMap中的Node节点,而且还新增了Entry before和Entry after,这两个指针。这样LinkedHashMap中所有的节点构成了一个双向的链表。同时在LinkedhashMap中,维护了两个属性Entry head, Entry tail,指向链表的头和尾巴。
同时还维护了一个final boolean accessOrder;如果该字段为true,则调用get方法后,会将该节点放入尾巴。
维护双向链表
linkedhashmap维护了一个双向链表,这样在对linkedhashmap中的元素进行增加、删除、获取时就要新增额外的操作,维护这个链表。
afterNodeRemoval(Node<K,V> e)方法
当linkedhashmap中删除节点之后,调用该方法,将e从维护的双链表中删除。
/**
* afterNodeRemoval此方法是将节点e从list中删除,而调用LinkedHashMap的remove方法实际调用的hashMap.remove方法已经将该节点从hashMap的桶中删除。
* (注意看前面的LinkedHashMap数据结构示意图,有两套指针(双向链表和桶中的单链表),
* 一套是HashMap中的hash桶内元素连接的黑色指针(即桶中的链表),只要把节点上下关系移除即可
* 另外一套是有颜色的指针,它是维护list的指针,从hash桶中删除后,还有从list中删除)
*/
void afterNodeRemoval(Node<K,V> e) {
// 标记节点e、e.before、e.after
LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
b<--->p<--->a。p是要删除的节点,b是前驱,a是后一个节点。即在删除时,先将其的前驱和后继定位好。
// 第一步:p的前后置空,选择e已经从链表中移除
p.before = p.after = null;
// 第二步:判断是否b为null,如果b为null,说明p是头节点,那么删除后,头节点就是a。如果b不为null,那么将b的后继指针指向a。
if (b == null) //这里一定要注意判断b是空,即p是head的情况。
head = a;
else
// 否则更新e的前驱.after == e的后继节点
b.after = a;
// 第三步:将e的后继.before = e的前驱。同理也要考虑a为null即p为尾节点的情况。
if (a == null)
// 如果没删除前e.after后继 == null,说明e处于list的尾部,将tail指向e的前驱节点
tail = b;
else
// 否则更新e的后继.before = e的前驱
a.before = b;
}
afterNodeInsertion方法
在向map中put节点后,会调用该方法。且重写了removeEldestEntry方法后,该方法才生效,该方法会将链表头节点删除。
/**
* 按照之前的想法,插入一个节点后应该是将插入的节点放入tailde后面,但是这里并不是这样几的
*/
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
// LinkedHashMap中的removeEldestEntry方法永远返回false(方法体见下文),也就说这个本方法不会执行任何操作。。。
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true); //当removeEldestEntry返回为true时,会将第一个元素删除(会将最近被访问的放到链表尾巴,也就是说链表头的元素是最久的被访问的)
}
}
/**
* removeEldestEntry方法永远返回false,当重写了该方法后,即返回true时,afterNodeInsertion才有效
*/
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
afterNodeAccess方法
当执行linkedhashmap中的get操作或者put操作,即访问(包括修改)元素时候,且当accessOrder==true时,执行该方法,该方法将访问的节点放入末尾。
LinkedHashMap实现LRU
class LRUCache extends LinkedHashMap {
private int capacity;
public LRUCache(int capacity) {
//accessOrder为true
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return (int)super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > capacity;
}
}
TreeMap
维护了一颗红黑树(自平衡的二叉搜索树),即会对put进来的节点根据key进行排序,放入红黑树的某个位置。
每个节点Node的属性有:
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left; //左孩子
Entry<K,V> right; //右孩子
Entry<K,V> parent; //父节点
boolean color = BLACK; //该节点的颜色
.......
}