HashMap evict 放逐之旅
我不认识的evict
正在撸猫写代码的我,遇到了一个LinkedHashMap ConcurrentModifyException,真是让人头秃。问题排查过程也是费了一些力气(手动狗头),最后发现是我异步请求接口时,同步修改了同一个map,所以主流程代码在读这个map的时候,会偶发性的出现ConcurrentModifyException(如果有人感兴趣排查过程,评论区留言,后续更贴)。
就在我在解决这个问题的时候,就遇到了Map深复制这个情况,我需要copy一份map用于异步请求的时候用。智能的Intellij 告诉我,我可以在初始化的时候传入map,我一直很相信我的得力助手,所以就这样一顿操作,调试运行没有问题,晚上又可以早点下班了。
但深复制这个情况,我还是需要从源码的角度确认一下,就看了LinkedHashMap初始化的代码
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
各位看官这个m肯定没有问题,但是这第二个参数传了一个false,给的注释是
evict false when initially constructing this map, else true (relayed to method afterNodeInsertion).
机智的我查了一下这个单词的意思
evict这个变量当在map初始化的时候是false,其他情况为true,传递给afterNodeInsection方法。
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);
}
}
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
然后这removeEldestEntry给了一大串注释,我就总结一下:我提供了方式给你重写在添加一个新元素之后删除map中最老的元素。然后作者说了一句话
this is useful if the map represents a cache: it allows the map to reduce memory consumption by deleting stale entries.
对于这个Cache,我搜索了一下,发现LinkedHashMap 可以作为LRU算法实现的数据结构。
LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰
关于LRU算法,看官们可以自行学习了解。
小结
最后总结一下,evict这个变量当在map构造器传入指定map初始化的时候是false,其他情况为true,也即其他构造器创建map之后再调用put方法,该参数则为true。