LinkedHashMap是HashMap的子类,但可以保持元素 按插入或访问有序,这与TreeMap按键排序不同
基本用法
LinkedHashMap是HashMap的子类,但内部还有一个双向链表维护键值对的顺序,每个键值对既位于哈希表中,也位于这个双向链表中。
LinkedHashMap支持两种顺序:一种是插入顺序;还有一种是访问顺序。
插入顺序:先添加的在前面,后添加的在后面,修改操作不影响顺序;
访问顺序:对一个键进行get/put操作后,其对应的键值对会移到链表末尾,所以末尾的是最近访问的,最开始的最久没被访问。
LinkedHashMap有5个构造方法,其中4个都是按插入排序,只有一个构造方法可以指定按访问顺序:
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
accessOrder参数用来指定是否按照访问排序,如果true,就是按照访问书序。
默认情况下,LinkedHashMap是按照插入有序的:
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("a", "a");
linkedHashMap.put("b", "b");
linkedHashMap.put("c", "c");
linkedHashMap.put("b", "bbbb");
for (Map.Entry<String, String> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
输出为:
a:a
b:bbbb
c:c
修改b的值不会修改顺序
访问有序的例子:
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>(16,0.75f,true);
linkedHashMap.put("a", "a");
linkedHashMap.put("b", "b");
linkedHashMap.put("c", "c");
linkedHashMap.put("b", "bbbb");
for (Map.Entry<String, String> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
每次访问,都将键值对移动到末尾,输出为:
a:a
c:c
b:bbbb
什么时候希望按照访问有序呢?一种典型的应用就是LRU缓存
ps:缓存容量有限,不能无限存储所有数据,如果缓存满了,就需要一定的策略将老的数据清除,这一策略称为替换算法。
LRU是一种流行的替换算法,它的全称是Least Recently Used,即最近最少使用。它认为最近刚被使用过的很快再次被用的可能性最高,而最久没有被访问的很快再次被访问的可能性最低,所以优先被清理。
虽然LinkedHashMap没有对容量做限制,但它可以轻易做到,它有一个protected方法:
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
在元素添加到LinkedHashMap后,LinkedHashMap会调用这个方法,传递的参数是最久没有被访问的键值对,如果这个方法返回true,则这个最久的键值对就会被删除。LinkedHashMap的实现总是返回false,所以容量没有限制,但子类可以重写该方法,在满足一定的条件下返回true;
使用LinkedHashMap实现一个简单的LRU实现:
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private int maxEntries;
public LRUCache(int maxEntries) {
super(16, 0.75f, true);
this.maxEntries = maxEntries;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
}
使用这个缓存:
LRUCache<String, String> cache = new LRUCache<>(3);
cache.put("a", "a");
cache.put("b", "b");
cache.put("c", "c");
cache.put("a", "aaa");
cache.put("d", "d");
System.out.println(cache);
输出为:
{c=c, a=aaa, d=d}
ps:添加d的时候,最久没被访问的b会被删除