Java中的LinkedHashMap及LRU緩存機制詳解
LinkedHashMap
- 維持插入順序;如 (1,“a”) (2, “b”)(先插的先訪問)
- 維持訪問順序(將最近訪問的數(shù)據(jù)移到鏈表的尾部 LRU思想 afterNodeAccess(里面處理了Entry的before after屬性))
- 主要是底層維護了一個雙向鏈表
- 不能被克隆和序列化(HashMap可以)
- LinkedHashMap的put類似于HashMap的 ; LinkedKeyIterator里調(diào)用nextNode() 源碼716行 modcount fast-fail
實現(xiàn)LRU緩存機制
- Least Recently Used的縮寫,即最近最少使用
- LRU緩存機制類似LinkedHashMap的雙向鏈表
- LRU緩存:內(nèi)存訪問時,設計一個緩存(如內(nèi)存4G,其中1G設置為緩存),大小固定,讀取內(nèi)存數(shù)據(jù),首先會去找緩存是否命中
- 如果命中,直接返回
- 反之,未命中從內(nèi)存中讀取數(shù)據(jù),把數(shù)據(jù)繼續(xù)添加到緩存當中,
- 如果緩存已滿,刪除訪問時間最早的數(shù)據(jù)
代碼測試驗證看下面的LRU代碼。
1)使用LinkedHashMap實現(xiàn)LRU緩存
需要重寫removeEldestEntry方法,該方法:
a. 通過 返回結果 去刪除訪問 時間最早 的數(shù)據(jù)
b. map的size()與給定緩存的最大size比較,如果map.size > MaxSize,則return true
c. 參數(shù)Map.Entry<K,V> eldest
package collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
class LRUCache<K,V> extends LinkedHashMap<K,V>{
private int maxSize; //緩存的大小
//重寫
public LRUCache(int maxSize){
super(16, 0.75f, true);
this.maxSize = maxSize;
}
//protected
@Override
protected boolean removeEldestEntry(Map.Entry<K,V> eldest){
return size() > maxSize;//根據(jù)大小,決定是否刪除最早結點
}
}
public class Teacher_1_15_LinkedHashMap {
public static void main(String[] args) {
//使用LinkedHashMap實現(xiàn)LRU緩存
//大小為3
LRUCache<String, String> cache = new LRUCache<>(3);
cache.put("a", "jfdshjhf");
//a
cache.put("b", "fdjhfjf");
//a - > b
cache.put("c", "all");
//a -> b -> c
cache.get("a");
//b -> c -> a
cache.put("d", "tulun");
//c -> a -> d
System.out.println(cache);
//維護插入順序
// LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
// map.put(1, "ajhd");
// map.put(5, "fsd");
// map.put(12, "ref");
// map.put(10, "gdfs");
// map.put(19, "hgf");
// map.put(21, "hgf");
//
// Iterator<Integer> iterator = map.keySet().iterator();
// while(iterator.hasNext()){
// System.out.println(iterator.next());
// }
// //維持訪問順序 加載因子:f類型 accessOrder
// LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>(16, 0.75f, true);
// map.put(1, "ajhd");
// map.put(5, "fsd");
// map.put(12, "ref");
// map.put(10, "gdfs");
// map.put(19, "hgf");
// map.put(21, "hgf");
//
// map.get(12);
// map.get(10);
//
// Iterator<Integer> iterator = map.keySet().iterator();
// while(iterator.hasNext()){
// System.out.println(iterator.next());
// }
}
}2)簡單的自定義實現(xiàn)(筆試)
設置頭結點 尾節(jié)點
存數(shù)據(jù)的容器:HashMap
維護雙向鏈表,確保 數(shù)據(jù)按照訪問時間存儲
package collection;
import java.util.HashMap;
import javax.swing.text.html.HTMLDocument.Iterator;
class MyLRUCache<K,V>{
private Node<K,V> head = new Node<>();//頭結點 無參構造
private Node<K,V> tail = new Node<>();//尾
private final HashMap<Integer,Node> hashMap = new HashMap<>();//new了,作為容器
private int size; //實際大小
private int capacity;//最大容量
class Node<K, V>{
private K key;
private V value;
private Node pre;//雙向鏈表
private Node next;
//構造器
public Node(){
}
public Node(K key, V value){
this.key = key;
this.value = value;
}
}
//初始化
public MyLRUCache(int capacity){
head.next = tail;//先指向無效的結點,專門指向 頭和尾
tail.pre = head;
this.capacity = capacity;
size = 0;
}
//添加某個節(jié)點(尾插法)
private void add(Node node){
//put里已判斷key是否存在,這里只管添加
//空表
if(tail.pre==head) {
head.next=node;//由head指向node
//node為首結點,前面再無,故不用設置node.pre
tail.pre=node;
}else {
//Tail之后添加
node.pre=tail.pre;
tail.pre.next=node;
tail.pre=node;
}
}
//刪除某個節(jié)點
private void remove(Node node){
//非空表
if(head.next!=tail) {//比地址
//頭結點
if(node==head.next) {
head.next=head.next.next; //新頭結點
//head.next.pre=null; //新頭結點不需要設置pre
}else if(node==tail.pre) {
//尾結點
tail.pre=tail.pre.pre;//新尾結點
//tail.pre.pre.next=null;
}else {
//其他
node.pre.next=node.next;//要刪除結點的前面結點的next指針往后指一個
node.next.pre=node.pre;//要刪除結點的后面結點的 pre指針往前指一個
}
}
}
//添加key-value鍵值對
public void put(K key, V value){
Node node = hashMap.get(key);//hashMap里已存有相同key的結點
if(node != null){
node.value = value;
//從鏈表中刪除node節(jié)點
remove(node);
//再將node節(jié)點尾插法重新插入鏈表(因為它剛被訪問)
add(node);
}else{
if(size < capacity){
size++;
}else{
//超容量(1個),刪除緩存中最近最少使用的節(jié)點head.next
//注意先從HashMap里刪除,不然remove會改變head.next值
hashMap.remove(head.next.key);//刪除指定key的結點
remove(head.next);//head.next變?yōu)橹赶騢ead.next.next了
}
//將newNode尾插法插入鏈表
Node newNode = new Node(key, value);//將輸入的key value包裝成node
hashMap.put((Integer) key, newNode);//存到hashMap,鍵為隨機值,值為node結點 (int)(Math.random()*100)
add(newNode);
}
}
//獲取value(返回8,表示成功)(將訪問的元素移到鏈表尾)
public int get(K key){
Node node = hashMap.get(key);
if(node == null){
return -1;
}
//刪除node
hashMap.remove(node.key);//刪除指定key的結點
remove(node);
//尾插法重新插入
add(node);
hashMap.put((Integer) key, node);
//等價于個數(shù)沒變
return 8;
}
public void show() {
Node<K,V> temp = head;
while(temp!=tail.pre){//tail.pre指向的是末尾節(jié)點
temp=temp.next;
System.out.print(temp.key+" ");
}
System.out.println();
}
}
public class Teacher_1_15_MyLRUCache {
public static void main(String[] args) {
//維護插入順序
MyLRUCache<Integer, String> cache = new MyLRUCache<>(3);
cache.put(1, "ajhd");
cache.put(5, "fsd");
cache.put(12, "ref");
cache.show();//應該1 5 12
System.out.println(cache.get(5));//成功則返回8
cache.show(); //應該1 12 5
cache.put(10, "gdfs");
cache.show();//應該12 5 10
//如何打印雙向鏈表里的順序
System.out.println(cache.get(1));
}
}到此這篇關于Java中的LinkedHashMap及LRU緩存機制詳解的文章就介紹到這了,更多相關LinkedHashMap及LRU緩存機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java修飾符abstract與static及final的精華總結
abstract、static、final三個修飾符是經(jīng)常會使用的,對他們的概念必須非常清楚,弄混了會產(chǎn)生些完全可以避免的錯誤,比如final和abstract不能一同出現(xiàn),static和abstract不能一同出現(xiàn),下面我們來詳細了解2022-04-04
java中Base64字符串出現(xiàn)不合法字符的問題解決
非法的base64數(shù)據(jù)可能導致編碼或解碼過程出錯,本文主要介紹了java中Base64字符串出現(xiàn)不合法字符的問題解決,具有一定的參考價值,感興趣的可以了解一下2024-06-06
javax.validation包里@NotNull等注解的使用方式
這篇文章主要介紹了javax.validation包里@NotNull等注解的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
SpringBoot使用@Cacheable注解實現(xiàn)緩存功能流程詳解
最近一直再學Spring Boot,在學習的過程中也有過很多疑問。為了解答自己的疑惑,也在網(wǎng)上查了一些資料,以下是對@Cacheable注解的一些理解2023-01-01
springBoot項目集成quartz開發(fā)定時任務案例及注意事項
這篇文章主要介紹了springBoot項目集成quartz開發(fā)定時任務案例及注意事項,這些功能的主要接口(API)是Scheduler接口。它提供了簡單的操作,例如:將任務納入日程或者從日程中取消,開始/停止/暫停日程進度,需要的朋友可以參考下2022-06-06
SpringCloud項目中Feign組件添加請求頭所遇到的坑及解決
這篇文章主要介紹了SpringCloud項目中Feign組件添加請求頭所遇到的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
Java成員變量與局部變量(動力節(jié)點Java學院整理)
這篇文章主要介紹了Java成員變量與局部變量的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-04-04

