public class LinkedHashMap
的两个使用技巧
public LinkedHashMap(int initialCapacity)
这是linkedHashMap的一个构造方法,从名字上来看,是初始化长度。该构造直接调用supper(initialCapacity),HashMap里对这个参数的解释是:它参与了一些与负载因子有关运算。
this.threshold = tableSizeFor(initialCapacity);//457行
所以尽量设置为预估长度。
如果没有其他的操作,我们在使用过程中,这个长度的作用是看不出来的。如下代码。
public static void main(String[] args) {
LinkedHashMap map = new LinkedHashMap(5);
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.put(4, 4);
map.put(5, 5);
map.put(6, 6);
System.out.println(map);//{1=1, 2=2, 3=3, 4=4, 5=5, 6=6}
}
尽管我们设定为5,但是现在map的长度还是6个,并不因为我们的设定而不让Put。
但是我们可以让linkedHashMap容量就是为5且先进先出。让上面代码里的打印变成
{2=2, 3=3, 4=4, 5=5, 6=6}。
看如下代码:
public class MyMap extends LinkedHashMap{
private int capacity;
public MyMap(int initialCapacity) {
super(initialCapacity);
this.capacity = initialCapacity;
}
@Override
protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
return size() > capacity;
}
}
我们新建了一个类,继承了linkedHashMap,重写了一个方法而已。
然后测试一下这个map
public static void main(String[] args) {
MyMap map = new MyMap(5);
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.put(4, 4);
map.put(5, 5);
map.put(6, 6);
System.out.println(map);//{2=2, 3=3, 4=4, 5=5, 6=6}
}
ok,打印了{2=2, 3=3, 4=4, 5=5, 6=6},变成了先进先出的FIFO结构。
原理解析:
原因在put方法里面。追查put,最终来到了HashMap的putVal的方法里,看下该方法的最后几行源码:
......
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
关键在afterNodeInsertion(evict)里面。evict就是设定好的true。
接着进入到afterNodeInsertion方法里,发现上面都没有,这分明就是put结束之后让子类重写一些特殊操作的节奏啊。再查看LinkedHashMap的afterNodeInsertion:
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
这里的意思是如果 true 且 第一个参数不是null 且 removeEldestEntry返回true ,那么就把第一个删除了。
我们再看下LinkedHashMap的removeEldestEntry方法,发现只返回了false,也就是说LinkedHashMap永远不会触发这个方法。
但是我们自己写的MyMap又重写了removeEldestEntry这个方法,返回判断 Map.size() 是否大于设定长度 capacity。
那么使用MyMap如果超出了长度就会返回true,则LinkedHashMap的 afterNodeInsertion方法就会删除掉第一个,最终形成了固定长度,先进先出的功能。
需求是设计一个Map,能够知道哪些元素是最近使用(存取)的,哪些元素是最少使用的。可能有些同学就在元素里面添加了时间戳了。但是LinkedHashMap提供了一种高效的方式来实现了这个功能,我们使用这个构造。
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder)
将accessOrder设置为true。
public static void main(String[] args) {
LinkedHashMap map = new LinkedHashMap(1000, 0.75f, true);
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.put(4, 4);
map.put(5, 5);
map.put(6, 6);
System.out.println(map);//{1=1, 2=2, 3=3, 4=4, 5=5, 6=6}
map.put(5, 55);
System.out.println(map);//{1=1, 2=2, 3=3, 4=4, 6=6, 5=55}
map.get(2);
System.out.println(map);//{1=1, 3=3, 4=4, 6=6, 5=55, 2=2}
}
可以看到无论是存还是取,都会把当前元素从当前位置删除,放到最后。
那么这个Map放在最后的永远是最近使用的,最前面的则是最少使用的。
原理:
LinkedHashMap重写了afterNodeAccess这个方法。afterNodeAccess的作用就是把当前节点删除,然后放到最后面。
而如果构造里的accessOrder如果为true,则会出现以下情况:
在get时会触发afterNodeAccess方法,在put(Key,Val)时,如果已经存在key,也会触发afterNodeAccess。