最近在用Map来做缓存的时候,考虑到内存问题,想做一个固定size的Map,当缓存条目数量达到该size的时候,新插进一个条目就删除Map里面最旧的一条条目来保证Map的size不会无限增长。查了一下资料,发现用LinkedHashMap来实现这个功能是意外的简单。
先来看看LinkedHashMap有什么特点:
LinkedHashMap:用链表实现来扩展HashMap;在LinkedHashMap中,使用无参构造方法创建LinkedHashMap时,条目按照他们插入的顺序排序,后插入的条目被放在LinkedHashMap的末尾,使用构造方法LinkedHashMap(initialCapacity, loadFactor, true)创建LinkedHashMap时,条目按照访问顺序排序,最近(晚)被访问的条目被放在LinkedHashMap的末尾
如上所述,LinkedHashMap中的条目是有序的,而且无论是插入顺序还是访问顺序的LinkedHashMap,它的最旧的那一条条目都位于链表的最前端。那么要实现需求的Map,最关键的就是在插入新条目的同时移除这一条最旧的条目。
想想觉得也不是什么难事,但是看了LinkedHashMap的源码之后发现这比想象中的要简单得多,因为LinkedHashMap提供了这样的一个方法:removeEldestEntry。从方法名就看得出这与我想做的大概就是同一件事!再看一下方法说明:
我可以十分肯定并且确定这就是我想要的!!!连使用例子都给出来的,简直不能更贴心了。
没啥好说的了,按照上面的例子来:
/**
* Created by chenrong on 2016/11/5.
* 维持此Map只保存MAX_ENTRIES个条目的稳定状态,在每次添加新条目时删除最旧的条目。
* 在尝试获取被删除的旧条目时会返回null
*/
public class MaxEntriesLinkedHashMap<K, V> extends LinkedHashMap<K, V>{
private final int MAX_ENTRIES;
public MaxEntriesLinkedHashMap(int MAX_ENTRIES) {
this.MAX_ENTRIES = MAX_ENTRIES;
}
public MaxEntriesLinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder, int MAX_ENTRIES) {
super(Math.min(initialCapacity, MAX_ENTRIES), loadFactor, accessOrder);
this.MAX_ENTRIES = MAX_ENTRIES;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > MAX_ENTRIES;
}
}
要做的就是继承LinkedHashMap并重写removeEldestEntry方法,当Map的size大于指定的数值时,返回true就行了,LinkedHashMap会自动删除Map里面最旧的条目。当然,就像方法描述里面提到的,你也可以自己编写删除该条目(作为参数传进来了)的逻辑,但此时方法必须返回false。
需要测试一下是否正确的删除了期望中最旧的那条条目
- 当Map时插入排序时,测试是否删除了最早被插入的条目
public class Test {
public static void main(String[] args) {
MaxEntriesLinkedHashMap<String, String> melhm = new MaxEntriesLinkedHashMap<>(3);
melhm.put("1", "1");
System.out.println(melhm);
melhm.put("2", "2");
System.out.println(melhm);
melhm.put("3", "3");
System.out.println(melhm);
System.out.println(melhm.get("1"));
melhm.put("4", "4");
System.out.println(melhm);
}
}
运行输出:
{1=1}
{1=1, 2=2}
{1=1, 2=2, 3=3}
1
{2=2, 3=3, 4=4}
Process finished with exit code 0
可以看到,指定Map的最大size为3时,插入了条目1,条目2,条目3并访问了条目1后,再插入条目4时,最早被插入的条目1被删除了,即使它是最近被访问的,而最不经常被访问的条目2仍然保留,测试通过。
- 当Map时访问排序时,测试是否删除了最不经常被访问的条目
public class Test {
public static void main(String[] args) {
MaxEntriesLinkedHashMap<String, String> melhm = new MaxEntriesLinkedHashMap<>(3, 0.75f, true, 3);
melhm.put("1", "1");
System.out.println(melhm);
melhm.put("2", "2");
System.out.println(melhm);
melhm.put("3", "3");
System.out.println(melhm);
System.out.println(melhm.get("1"));
melhm.put("4", "4");
System.out.println(melhm);
}
}
运行输出:
{1=1}
{1=1, 2=2}
{1=1, 2=2, 3=3}
1
{3=3, 1=1, 4=4}
Process finished with exit code 0
可以看到,指定Map的最大size为3时,插入了条目1,条目2,条目3并访问了条目1后,再插入条目4时,最不经常被访问的条目2被删除了,而最早被插入的条目1仍然保留,因为它最近被访问过,测试通过。