1key和value都允许为空;2key重复会覆盖,value可以重复;3有序的;4LinkedHashMap是非线程安全的;
1 LinkedHashMap可以认为是HashMap+LinkedList,也就是说,它使用HashMap操作数据结构,也用LinkedList维护插入元素的先后顺序. 2 LinkedHashMap的实现思想就是多态,理解LinkedHashMap能帮助我们加深对多态的理解.
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
而它比HashMap多了两个属性:
//链表的头结点
private transient Entry<K,V> header;
//该属性指取得键值对的方式,是个布尔值:false表示插入顺序;true表示访问顺序;
private final boolean accessOrder;
LinkedHashMap有五个构造器:
//用默认的初始容量和负载因子构建一个LinkedHashMap,取出键值对的方式是插入顺序
public LinkedHashMap() {
super();
accessOrder = false;
}
//构造一个指定初始容量的LinkedHashMap,取得键值对的顺序
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
//构造一个指定初始容量和负载因子,按照插入顺序的LinkedHashMap
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
//根据给定的初始容量,负载因子和键值对迭代顺序构建一个LinkedHashMap
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
//通过给定的map创建一个LinkedHashMap,负载因子是默认值,迭代方式是插入顺序.
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
从构造方法可以看出,默认都是采用插入顺序来维持取出键值对的次序.所有的构造方法都是通过父类的构造方法来建造对象的.
LinkedHashMap和HashMap的区别在于他们的基本数据机构上,我们来看一下LinkedHashMap的基本数据结构Entry:
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// These fields comprise the doubly linked list used for iteration.
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
next是用于维护HashMap指定table位置上连接的Entry顺序的;before、after是用于维护Entry插入的先后顺序的.正是因为before、after和header的存在,LinkedHashMap才形成了循环双向链表.需要注意的是,header节点,是LinkedHashMap的一个属性,它并不保存key-value内容,它是双向链表的入口.
利用LinkedHashMap实现LRU算法缓存
所谓LRU:Least Recently Used,最近最少使用,即当缓存了,会优先淘汰那些最近不常访问的数据.即冷数据优先淘汰.
我们来看看LinkedHashMap的一个构造方法:
//根据给定的初始容量,负载因子和键值对迭代顺序构建一个LinkedHashMap
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
在这个构造方法中,有个accessOrder,它不同的值有不同的意义:
false, 所有的Entry按照插入的顺序排列
true, 所有的Entry按照访问的顺序排列
访问的顺序:如果有1 2 3这3个Entry,那么访问了1,就把1移到尾部去,即2 3 1。每次访问都把访问的那个数据移到双向队列的尾部去,那么每次要淘汰数据的时候,双向队列最头的那个数据不就是最不常访问的那个数据了吗?换句话说,双向链表最头的那个数据就是要淘汰的数据。
import java.util.*;
public class Solution {
/**
* lru design
* @param operators int整型二维数组 the ops
* @param k int整型 the k
* @return int整型一维数组
*/
public int[] LRU (int[][] operators, int k) {
// write code here
ArrayList<Integer> list = new ArrayList<>();
LRUCache lru = new LRUCache(k);
for(int[] opt:operators){
if(opt[0] == 1){
lru.put(opt[1],opt[2]);
}else{
list.add(lru.get(opt[1]));
}
}
int[] res = new int[list.size()];
int i = 0;
for(int val:list){
res[i] = list.get(i);
i++;
}
return res;
}
}
//设置LRU缓存结构
class LRUCache{
int cap;
LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>();
public LRUCache(int capactity){
this.cap = capactity;
}
// 将key变为最近使用
private void makeRecently(int key){
int val = cache.get(key);
//删除key,重新插入到队尾
cache.remove(key);
cache.put(key, val);
}
//获取值
public int get(int key){
if(!cache.containsKey(key)){
return -1;
}
//将这个key变为最近使用的
makeRecently(key);
return cache.get(key);
}
//存进值
public void put(int key,int val){
if(cache.containsKey(key)){
cache.put(key, val);
//设置为最近使用
makeRecently(key);
return;
}
//超出缓存的大小
if(cache.size() >= this.cap){
//拿到链表头部的key(其最久未使用的key)
int oldstKet = cache.keySet().iterator().next();
cache.remove(oldstKet);
}
//将新的key添加到链表尾部
cache.put(key,val);
}
}