LinkedHashMap源码分析
2018-07-06 本文已影响0人
丹青水
1 集合特性
对于集合框架关注点:
- 集合底层实现的数据结构是什么 数组+链表+红黑树
- 集合中元素是否允许为空 是
- 是否允许重复的数据 否
- 是否有序(这里的有序是指读取数据和存放数据的顺序是否一致) 是
- 是否线程安全。 否
2 LinkedHashMap分析
LinkedHashMap 继承HashMap
没有重写put resize等方法 因此基本数据结构是相同的数组、链表、红黑树
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
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
public LinkedHashMap() {
super();//和hashMap一样
accessOrder = false;//表示顺序类型,true查询,false表示插入
}
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
final boolean accessOrder;
分析下put方法
put方法没有重写,因此和HashMap是一样的 重写了newNode
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;
}
//这里可以对比下hashMap,少了 linkNodeLast(p)
//这个地方维护一个插入的头尾关系,本质可以结合LinkedList的实现很相似
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实现了afterNodeAccess,afterNodeInsertion方法,这个都依赖accessOrder这个参数,如果是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, 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 afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
3 LinkedHashMap和LRU缓存
LRU:最近最少使用算法
来看个例子:
LinkedHashMap linkedHashMap=new LinkedHashMap();
linkedHashMap.put(1, 1);
linkedHashMap.put(2, 2);
linkedHashMap.put(3, 3);
linkedHashMap.put(4, 4);
linkedHashMap.put(3, 5);
linkedHashMap.get(4);
System.out.println(linkedHashMap.toString());
//输出结果为{1=1, 2=2, 3=5, 4=4}没啥问题
我们改变accessOrder的查询方式
LinkedHashMap linkedHashMap1= new LinkedHashMap(16, 0.75f, true);
linkedHashMap1.put(1,1);
linkedHashMap1.put(2,2);
linkedHashMap1.put(3, 3);
linkedHashMap1.put(4, 4);
System.out.println(linkedHashMap1.get(2));
System.out.println(linkedHashMap1.get(1));
System.out.println(linkedHashMap1.toString());
//运行结果{3=3, 4=4, 2=2, 1=1}
那么我们可以根据LinkedHashMap这个特性写一个简单的LRU缓存
public class LruMap<K,V> extends LinkedHashMap<K,V>{
private int maxSize;
public LruMap(int maxSize) {
super(16,0.75f,true);
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size()>maxSize;
}
public static void main(String[] args){
LruMap<Integer,Integer> lruMap=new LruMap<>(3);
lruMap.put(1,1);
lruMap.put(2,2);
lruMap.put(3,3);
lruMap.put(4,4);
System.out.println(lruMap.toString());
//运行结果{2=2, 3=3, 4=4} 发现4已经把3替换掉了
}
}
ps:我们知道LinkedHashMap是非线程安全的,所以LruMap这个缓存在多线程环境下是有问题的,我们可以重写起增删改的方法,加上synchronized关键字或者使用ReentrantLock机制解决并发问题~