淺談Java如何實(shí)現(xiàn)一個(gè)基于LRU時(shí)間復(fù)雜度為O(1)的緩存
LRU:Least Recently Used最近最少使用,當(dāng)緩存容量不足時(shí),先淘汰最近最少使用的數(shù)據(jù)。就像JVM垃圾回收一樣,希望將存活的對(duì)象移動(dòng)到內(nèi)存的一端,然后清除其余空間。
緩存基本操作就是讀、寫(xiě)、淘汰刪除。
讀操作時(shí)間復(fù)雜度為O(1)的那就是hash操作了,可以使用HashMap索引 key。
寫(xiě)操作時(shí)間復(fù)雜度為O(1),使用鏈表結(jié)構(gòu),在鏈表的一端插入節(jié)點(diǎn),是可以完成O(1)操作,但是為了配合讀,還要再次將節(jié)點(diǎn)放入HashMap中,put操作最優(yōu)是O(1),最差是O(n)。
不少童鞋就有疑問(wèn)了,寫(xiě)入時(shí)又使用map進(jìn)行了put操作,為何緩存不直接使用map?沒(méi)錯(cuò),首先使用map存儲(chǔ)了節(jié)點(diǎn)數(shù)據(jù)就是采用空間換時(shí)間,但是淘汰刪除不好處理,使用map如何去記錄最近最少使用(涉及到時(shí)間、頻次問(wèn)題)。so,使用鏈表可以將活躍節(jié)點(diǎn)移動(dòng)到鏈表的一端,淘汰時(shí)直接從另一端進(jìn)行刪除。
public class LruCache<K,V> { /** 這里簡(jiǎn)單點(diǎn)直接初始化了*/ private int capacity = 2; private int size = 0; private HashMap<K,DoubleListNode<K,V>> cache = new HashMap<>(capacity); private DoubleListNode<K,V> lruNode = new DoubleListNode<K, V>(null,null,null,null); private DoubleListNode<K,V> mruNode = new DoubleListNode<K, V>(null,null,null,null); public V get(K key){ DoubleListNode<K,V> target = cache.get(key); if (target == null) { return null; } /** 使用過(guò)就移動(dòng)到右側(cè) */ move2mru(target); return target.value; } public void put(K key,V value){ if(cache.containsKey(key)){ DoubleListNode<K,V> temp = cache.get(key); temp.value = value; /** 使用過(guò)就移動(dòng)到右側(cè) */ move2mru(temp); return; } /** 容量滿了清除左側(cè) */ if(size >= capacity){ evict4lru(); } DoubleListNode<K,V> newNode = new DoubleListNode<>(mruNode,null,key,value); if(size == 0){ lruNode.next = newNode; } mruNode.next = newNode; mruNode = newNode; cache.put(key,newNode); size++; } private void move2mru(DoubleListNode<K,V> newMru){ DoubleListNode<K,V> pre = newMru.pre; DoubleListNode<K,V> next = newMru.next; pre.next = next; newMru.pre = mruNode; mruNode.next = newMru; mruNode = newMru; } private void evict4lru(){ cache.remove(lruNode.next.key); lruNode.next = lruNode.next.next; size--; } public String toString(){ StringBuffer sb = new StringBuffer("lru -> "); DoubleListNode<K,V> temp = lruNode; while(temp!=null){ sb.append(temp.key).append(":").append(temp.value); sb.append(" -> "); temp = temp.next; } sb.append(" -> mru "); return sb.toString(); } public static void main(String[] args) { LruCache<String,String> cache = new LruCache<>(); cache.put("1","1"); System.out.println(cache); cache.get("1"); cache.put("2","2"); System.out.println(cache); cache.put("3","3"); System.out.println(cache); cache.put("4","4"); System.out.println(cache); } } class DoubleListNode<K,V>{ K key; V value; DoubleListNode<K,V> pre; DoubleListNode<K,V> next; public DoubleListNode(K key,V value){ this.key = key; this.value = value; } public DoubleListNode(DoubleListNode<K,V> pre,DoubleListNode<K,V> next,K key,V value){ this.pre = pre; this.next = next; this.key = key; this.value = value; } }
這里使用鏈表,及HashMap完成了基于LRU的緩存,其中HashMap主要用來(lái)快速索引key,鏈表用來(lái)完成LRU機(jī)制。當(dāng)然尚有許多不足,包括緩存移除remove,緩存ttl,線程安全等。
到此這篇關(guān)于淺談Java如何實(shí)現(xiàn)一個(gè)基于LRU時(shí)間復(fù)雜度為O(1)的緩存的文章就介紹到這了,更多相關(guān)Java基于LRU時(shí)間復(fù)雜度為O(1)的緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java 數(shù)據(jù)結(jié)構(gòu)之時(shí)間復(fù)雜度與空間復(fù)雜度詳解
- java實(shí)現(xiàn)堆排序以及時(shí)間復(fù)雜度的分析
- Java 關(guān)于時(shí)間復(fù)雜度和空間復(fù)雜度的深度刨析
- Java時(shí)間復(fù)雜度、空間復(fù)雜度的深入詳解
- Java算法之時(shí)間復(fù)雜度和空間復(fù)雜度的概念和計(jì)算
- Java數(shù)據(jù)結(jié)構(gòu)--時(shí)間和空間復(fù)雜度
- Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之時(shí)間復(fù)雜度與空間復(fù)雜度
相關(guān)文章
Java+Ajax實(shí)現(xiàn)的用戶名重復(fù)檢驗(yàn)功能實(shí)例詳解
這篇文章主要介紹了Java+Ajax實(shí)現(xiàn)的用戶名重復(fù)檢驗(yàn)功能,結(jié)合實(shí)例形式詳細(xì)分析了java針對(duì)用戶名提交的ajax數(shù)據(jù)庫(kù)查詢與重復(fù)檢查功能相關(guān)實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下2018-12-12詳解Java中NullPointerException異常的原因詳解以及解決方法
這篇文章主要介紹了詳解Java中NullPointerException異常的原因詳解以及解決方法。文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08詳解SpringBoot簡(jiǎn)化配置分析總結(jié)
這篇文章主要介紹了詳解SpringBoot簡(jiǎn)化配置分析總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Java實(shí)現(xiàn)堆排序(Heapsort)實(shí)例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)堆排序(Heapsort)實(shí)例代碼,有需要的朋友可以參考一下2013-12-12基于java時(shí)區(qū)轉(zhuǎn)換夏令時(shí)的問(wèn)題及解決方法
下面小編就為大家分享一篇基于java時(shí)區(qū)轉(zhuǎn)換夏令時(shí)的問(wèn)題及解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11確保SpringBoot定時(shí)任務(wù)只執(zhí)行一次的常見(jiàn)方法小結(jié)
在Spring Boot項(xiàng)目中,確保定時(shí)任務(wù)只執(zhí)行一次是一個(gè)常見(jiàn)的需求,這種需求可以通過(guò)多種方式來(lái)實(shí)現(xiàn),以下是一些常見(jiàn)的方法,它們各具特點(diǎn),可以根據(jù)項(xiàng)目的實(shí)際需求來(lái)選擇最合適的方法,需要的朋友可以參考下2024-10-10Springmvc發(fā)送json數(shù)據(jù)轉(zhuǎn)Java對(duì)象接收
這篇文章主要介紹了Springmvc發(fā)送json數(shù)據(jù)轉(zhuǎn)Java對(duì)象接收,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10使用eclipse導(dǎo)入javaWeb項(xiàng)目的圖文教程
這篇文章主要介紹了如何使用eclipse導(dǎo)入別人的javaWeb項(xiàng)目,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07解決Springboot項(xiàng)目打包后的頁(yè)面丟失問(wèn)題(thymeleaf報(bào)錯(cuò))
這篇文章主要介紹了解決Springboot項(xiàng)目打包后的頁(yè)面丟失問(wèn)題(thymeleaf報(bào)錯(cuò)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11