java中的緩存

所謂緩存,就是將程序或系統常常要調用的對象存在內存中,一遍其使用時能夠快速調用,沒必要再去建立新的重複的實例。這樣作能夠減小系統開銷,提升系統效率。 java

AD:WOT2015 互聯網運維與開發者大會 熱銷搶票 算法

Cache

Cache 數據庫

所謂緩存,就是將程序或系統常常要調用的對象存在內存中,一遍其使用時能夠快速調用,沒必要再去建立新的重複的實例。這樣作能夠減小系統開銷,提升系統效率。 緩存

緩存主要可分爲二大類:  安全

1、經過文件緩存,顧名思義文件緩存是指把數據存儲在磁盤上,無論你是以XML格式,序列化文件DAT格式仍是其它文件格式;   網絡

2、內存緩存,也就是實現一個類中靜態Map,對這個Map進行常規的增刪查.  數據結構

代碼以下 : 多線程

 
  1. package lhm.hcy.guge.frameset.cache; 
  2.  
  3. import java.util.*; 
  4.  
  5.  //Description: 管理緩存 
  6.  
  7.  //可擴展的功能:當chche到內存溢出時必須清除掉最先期的一些緩存對象,這就要求對每一個緩存對象保存建立時間 
  8.  
  9. public class CacheManager { 
  10.     private static HashMap cacheMap = new HashMap(); 
  11.  
  12.     //單實例構造方法 
  13.     private CacheManager() { 
  14.         super(); 
  15.     } 
  16.     //獲取布爾值的緩存 
  17.     public static boolean getSimpleFlag(String key){ 
  18.         try
  19.             return (Boolean) cacheMap.get(key); 
  20.         }catch(NullPointerException e){ 
  21.             return false
  22.         } 
  23.     } 
  24.     public static long getServerStartdt(String key){ 
  25.         try { 
  26.             return (Long)cacheMap.get(key); 
  27.         } catch (Exception ex) { 
  28.             return 0
  29.         } 
  30.     } 
  31.     //設置布爾值的緩存 
  32.     public synchronized static boolean setSimpleFlag(String key,boolean flag){ 
  33.         if (flag && getSimpleFlag(key)) {//假如爲真不容許被覆蓋 
  34.             return false
  35.         }else
  36.             cacheMap.put(key, flag); 
  37.             return true
  38.         } 
  39.     } 
  40.     public synchronized static boolean setSimpleFlag(String key,long serverbegrundt){ 
  41.         if (cacheMap.get(key) == null) { 
  42.             cacheMap.put(key,serverbegrundt); 
  43.             return true
  44.         }else
  45.             return false
  46.         } 
  47.     } 
  48.  
  49.  
  50.     //獲得緩存。同步靜態方法 
  51.     private synchronized static Cache getCache(String key) { 
  52.         return (Cache) cacheMap.get(key); 
  53.     } 
  54.  
  55.     //判斷是否存在一個緩存 
  56.     private synchronized static boolean hasCache(String key) { 
  57.         return cacheMap.containsKey(key); 
  58.     } 
  59.  
  60.     //清除全部緩存 
  61.     public synchronized static void clearAll() { 
  62.         cacheMap.clear(); 
  63.     } 
  64.  
  65.     //清除某一類特定緩存,經過遍歷HASHMAP下的全部對象,來判斷它的KEY與傳入的TYPE是否匹配 
  66.     public synchronized static void clearAll(String type) { 
  67.         Iterator i = cacheMap.entrySet().iterator(); 
  68.         String key; 
  69.         ArrayList arr = new ArrayList(); 
  70.         try { 
  71.             while (i.hasNext()) { 
  72.                 java.util.Map.Entry entry = (java.util.Map.Entry) i.next(); 
  73.                 key = (String) entry.getKey(); 
  74.                 if (key.startsWith(type)) { //若是匹配則刪除掉 
  75.                     arr.add(key); 
  76.                 } 
  77.             } 
  78.             for (int k = 0; k < arr.size(); k++) { 
  79.                 clearOnly(arr.get(k)); 
  80.             } 
  81.         } catch (Exception ex) { 
  82.             ex.printStackTrace(); 
  83.         } 
  84.     } 
  85.  
  86.     //清除指定的緩存 
  87.     public synchronized static void clearOnly(String key) { 
  88.         cacheMap.remove(key); 
  89.     } 
  90.  
  91.     //載入緩存 
  92.     public synchronized static void putCache(String key, Cache obj) { 
  93.         cacheMap.put(key, obj); 
  94.     } 
  95.  
  96.     //獲取緩存信息 
  97.     public static Cache getCacheInfo(String key) { 
  98.  
  99.         if (hasCache(key)) { 
  100.             Cache cache = getCache(key); 
  101.             if (cacheExpired(cache)) { //調用判斷是否終止方法 
  102.                 cache.setExpired(true); 
  103.             } 
  104.             return cache; 
  105.         }else 
  106.             return null
  107.     } 
  108.  
  109.     //載入緩存信息 
  110.     public static void putCacheInfo(String key, Cache obj, long dt,boolean expired) { 
  111.         Cache cache = new Cache(); 
  112.         cache.setKey(key); 
  113.         cache.setTimeOut(dt + System.currentTimeMillis()); //設置多久後更新緩存 
  114.         cache.setValue(obj); 
  115.         cache.setExpired(expired); //緩存默認載入時,終止狀態爲FALSE 
  116.         cacheMap.put(key, cache); 
  117.     } 
  118.     //重寫載入緩存信息方法 
  119.     public static void putCacheInfo(String key,Cache obj,long dt){ 
  120.         Cache cache = new Cache(); 
  121.         cache.setKey(key); 
  122.         cache.setTimeOut(dt+System.currentTimeMillis()); 
  123.         cache.setValue(obj); 
  124.         cache.setExpired(false); 
  125.         cacheMap.put(key,cache); 
  126.     } 
  127.  
  128.     //判斷緩存是否終止 
  129.     public static boolean cacheExpired(Cache cache) { 
  130.         if (null == cache) { //傳入的緩存不存在 
  131.             return false
  132.         } 
  133.         long nowDt = System.currentTimeMillis(); //系統當前的毫秒數 
  134.         long cacheDt = cache.getTimeOut(); //緩存內的過時毫秒數 
  135.         if (cacheDt <= 0||cacheDt>nowDt) { //過時時間小於等於零時,或者過時時間大於當前時間時,則爲FALSE 
  136.             return false
  137.         } else { //大於過時時間 即過時 
  138.             return true
  139.         } 
  140.     } 
  141.  
  142.     //獲取緩存中的大小 
  143.     public static int getCacheSize() { 
  144.         return cacheMap.size(); 
  145.     } 
  146.  
  147.     //獲取指定的類型的大小 
  148.     public static int getCacheSize(String type) { 
  149.         int k = 0
  150.         Iterator i = cacheMap.entrySet().iterator(); 
  151.         String key; 
  152.         try { 
  153.             while (i.hasNext()) { 
  154.                 java.util.Map.Entry entry = (java.util.Map.Entry) i.next(); 
  155.                 key = (String) entry.getKey(); 
  156.                 if (key.indexOf(type) != -1) { //若是匹配則刪除掉 
  157.                     k++; 
  158.                 } 
  159.             } 
  160.         } catch (Exception ex) { 
  161.             ex.printStackTrace(); 
  162.         } 
  163.  
  164.         return k; 
  165.     } 
  166.  
  167.     //獲取緩存對象中的全部鍵值名稱 
  168.     public static ArrayList getCacheAllkey() { 
  169.         ArrayList a = new ArrayList(); 
  170.         try { 
  171.             Iterator i = cacheMap.entrySet().iterator(); 
  172.             while (i.hasNext()) { 
  173.                 java.util.Map.Entry entry = (java.util.Map.Entry) i.next(); 
  174.                 a.add((String) entry.getKey()); 
  175.             } 
  176.         } catch (Exception ex) {} finally { 
  177.             return a; 
  178.         } 
  179.     } 
  180.  
  181.     //獲取緩存對象中指定類型 的鍵值名稱 
  182.     public static ArrayList getCacheListkey(String type) { 
  183.         ArrayList a = new ArrayList(); 
  184.         String key; 
  185.         try { 
  186.             Iterator i = cacheMap.entrySet().iterator(); 
  187.             while (i.hasNext()) { 
  188.                 java.util.Map.Entry entry = (java.util.Map.Entry) i.next(); 
  189.                 key = (String) entry.getKey(); 
  190.                 if (key.indexOf(type) != -1) { 
  191.                     a.add(key); 
  192.                 } 
  193.             } 
  194.         } catch (Exception ex) {} finally { 
  195.             return a; 
  196.         } 
  197.     } 
  198.  
  199.  
  200.  
  201. package lhm.hcy.guge.frameset.cache; 
  202.  
  203. public class Cache { 
  204.         private String key;//緩存ID 
  205.         private Object value;//緩存數據 
  206.         private long timeOut;//更新時間 
  207.         private boolean expired; //是否終止 
  208.         public Cache() { 
  209.                 super(); 
  210.         } 
  211.  
  212.         public Cache(String key, Object value, long timeOut, boolean expired) { 
  213.                 this.key = key; 
  214.                 this.value = value; 
  215.                 this.timeOut = timeOut; 
  216.                 this.expired = expired; 
  217.         } 
  218.  
  219.         public String getKey() { 
  220.                 return key; 
  221.         } 
  222.  
  223.         public long getTimeOut() { 
  224.                 return timeOut; 
  225.         } 
  226.  
  227.         public Object getValue() { 
  228.                 return value; 
  229.         } 
  230.  
  231.         public void setKey(String string) { 
  232.                 key = string; 
  233.         } 
  234.  
  235.         public void setTimeOut(long l) { 
  236.                 timeOut = l; 
  237.         } 
  238.  
  239.         public void setValue(Object object) { 
  240.                 value = object; 
  241.         } 
  242.  
  243.         public boolean isExpired() { 
  244.                 return expired; 
  245.         } 
  246.  
  247.         public void setExpired(boolean b) { 
  248.                 expired = b; 
  249.         } 
  250.  
  251. //測試類, 
  252. class Test { 
  253.     public static void main(String[] args) { 
  254.         System.out.println(CacheManager.getSimpleFlag("alksd")); 
  255. //        CacheManager.putCache("abc", new Cache()); 
  256. //        CacheManager.putCache("def", new Cache()); 
  257. //        CacheManager.putCache("ccc", new Cache()); 
  258. //        CacheManager.clearOnly(""); 
  259. //        Cache c = new Cache(); 
  260. //        for (int i = 0; i < 10; i++) { 
  261. //            CacheManager.putCache("" + i, c); 
  262. //        } 
  263. //        CacheManager.putCache("aaaaaaaa", c); 
  264. //        CacheManager.putCache("abchcy;alskd", c); 
  265. //        CacheManager.putCache("cccccccc", c); 
  266. //        CacheManager.putCache("abcoqiwhcy", c); 
  267. //        System.out.println("刪除前的大小:"+CacheManager.getCacheSize()); 
  268. //        CacheManager.getCacheAllkey(); 
  269. //        CacheManager.clearAll("aaaa"); 
  270. //        System.out.println("刪除後的大小:"+CacheManager.getCacheSize()); 
  271. //        CacheManager.getCacheAllkey(); 
  272.  
  273.  
  274.     } 
  275. }

java中的本地緩存,工做後陸續用到,一直想寫,一直無從下手,最近又涉及到這方面的問題了,梳理了一下。本身構造單例、guava、ehcache基本上涵蓋了目前的大多數行爲了。
 
爲何要有本地緩存?
在系統中,有些數據,數據量小,可是訪問十分頻繁(例如國家標準行政區域數據),針對這種場景,須要將數據搞到應用的本地緩存中,以提高系統的訪問效率, 減小無謂的數據庫訪問(數據庫訪問佔用數據庫鏈接,同時網絡消耗比較大),可是有一點須要注意,就是緩存的佔用空間以及緩存的失效策略。
 
爲何是本地緩存,而不是分佈式的集羣緩存?
     目前的數據,大可能是業務無關的小數據緩存,沒有必要搞分佈式的集羣緩存,目前涉及到訂單和商品的數據,會直接走DB進行請求,再加上分佈式緩存的構建,集羣維護成本比較高,不太適合緊急的業務項目。
     這裏介紹一下緩存使用的三個階段(摘自info架構師文檔)
      
 
本地緩存在那個區域?
     目前考慮的是佔用了JVM的heap區域,再細化一點的就是heap中的old區,目前的數據量來看,都是一些小數據,加起來沒有幾百兆,放 在heap區域最快最方便。後期若是須要放置在本地緩存的數據大的時候,能夠考慮在off-heap區域(direct-memory 或者 big-memory),可是off-heap區域的話,須要考慮對象的序列化(由於off-heap區域存儲的是二進制的數據),另一個的話就是 off-heap的GC問題。其實,若是真的數據量比較大,那其實就能夠考慮搞一個集中式的緩存系統,能夠是單機,也能夠是集羣,來承擔緩存的做用。
 
搞一個單例模式,裏面有個Map的變量來放置數據
關於單例模式,一個既簡單又複雜的模式(http://iamzhongyong.iteye.com/blog/1539642)
很是典型的代碼以下:
public class SingletonMap {
  //一個本地的緩存Map
  private Map localCacheStore = new HashMap(); 
 
  //一個私有的對象,非懶漢模式
  private static SingletonMap singletonMap = new SingletonMap(); 
 
  //私有構造方法,外部不能夠new一個對象
  private SingletonMap(){
  }  
 
  //靜態方法,外部得到實例對象
  public static SingletonMap getInstance(){
    return singletonMap;
  }
 
  //得到緩存中的數據
  public Object getValueByKey(String key){
    return localCacheStore.get(key);
  }
  //向緩存中添加數據
  public void putValue(String key , Object value){
    localCacheStore.put(key, value);
  }
}
這種能不能用?能夠用,可是很是侷限
可是這種的就是本地緩存了嗎?答案顯然不是,爲啥呢?
一、  沒有緩存大小的設置,沒法限定緩存體的大小以及存儲數據的限制(max size limit);
二、  沒有緩存的失效策略(eviction policies);
三、  沒有弱鍵引用,在內存佔用吃緊的狀況下,JVM是沒法回收的(weak rererences keys);
四、  沒有監控統計(statistics);
五、  持久性存儲(persistent store);
因此,這種就直接廢掉了。。。
 
引入EhCache來構建緩存(詳細介紹:  http://raychase.iteye.com/blog/1545906)
EhCahce的核心類:
A、CacheManager:Cache的管理類;
B、Cache:具體的cache類信息,負責緩存的get和put等操做
C、CacheConfiguration :cache的配置信息,包含策略、最大值等信息
D、Element:cache中單條緩存數據的單位
典型的代碼以下:
public static void main(String[] args) {
    //EhCache的緩存,是經過CacheManager來進行管理的
    CacheManager cacheManager = CacheManager.getInstance();
     
    //緩存的配置,也能夠經過xml文件進行
    CacheConfiguration conf = new CacheConfiguration();
    conf.name("cache_name_default");//設置名字
    conf.maxEntriesLocalHeap(1000);//最大的緩存數量
    conf.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU);//設置失效策略
     
    //建立一個緩存對象,並把設置的信息傳入進去
    Cache localCache = new Cache(conf);
     
    //將緩存對象添加到管理器中
    cacheManager.addCache(localCache);
         
    localCache.put(new Element("iamzhongyong", new Date()));
     
    System.out.println(localCache.getSize());
    System.out.println(localCache.getStatistics().toString());
    System.out.println(localCache.getName());
    System.out.println(localCache.get("iamzhongyong").toString());
    System.out.println(localCache.get("iamzhongyong").getObjectValue());   
  }
固然,Cache的配置信息,能夠經過配置文件制定了。。。
優勢:功能強大,有失效策略、最大數量設置等,緩存的持久化只有企業版纔有,組件的緩存同步,能夠經過jgroup來實現
缺點:功能強大的同時,也使其更加複雜
 
引入guava的cacheBuilder來構建緩存
這個很是強大、簡單,經過一個CacheBuilder類就能夠知足需求。
缺點就是若是要組件同步的話,須要本身實現這個功能。
典型的代碼以下:
public class GuavaCacheBuilderTest {
  public static void main(String[] args) throws Exception{
    GuavaCacheBuilderTest cache = new GuavaCacheBuilderTest();
    cache.getNameLoadingCache("bixiao");
  }
  public void getNameLoadingCache(String name) throws Exception{
    LoadingCache cache = CacheBuilder.newBuilder()     
      .maximumSize(20)//設置大小,條目數     
      .expireAfterWrite(20, TimeUnit.SECONDS)//設置失效時間,建立時間    
      .expireAfterAccess(20, TimeUnit.HOURS) //設置時效時間,最後一次被訪問     
      .removalListener(new RemovalListener() { //移除緩存的監聽器
        public void onRemoval(RemovalNotification notification) {
          System.out.println("有緩存數據被移除了");
        }})
      .build(new CacheLoader(){ //經過回調加載緩存
        @Override
        public String load(String name) throws Exception {
          return name + "-" + "iamzhongyong";
        }
    });
    System.out.println(cache.get(name));
    //cache.invalidateAll();
  }
}
 
緩存預熱怎麼搞?
A、全量預熱,固定的時間段移除全部,而後再全量預熱
適用場景:
一、數據更新不頻繁,例如天天晚上3點更新便可的需求;
 二、數據基本沒有變化,例如全國區域性數據;
B、增量預熱(緩存查詢,沒有,則查詢數據庫,有則放入緩存)
適用場景:
一、  數據更新要求緩存中同步更新的場景
 
集羣內部,緩存的一致性如何保證?
若是採用ehcache的話,可使用框架自己的JGroup來實現組內機器之間的緩存同步。
若是是採用google的cacheBuilder的話,須要本身實現緩存的同步。
A、非實時生效數據:數據的更新不會時時發生,應用啓動的時候更新便可,而後定時程序定時去清理緩存;
B、須要實時生效數據:啓動時可預熱也可不預熱,可是緩存數據變動後,集羣之間須要同步

LRU是Least Recently Used 的縮寫,翻譯過來就是「最近最少使用」,LRU緩存就是使用這種原理實現,簡單的說就是緩存必定量的數據,當超過設定的閾值時就把一些過時的數據刪除掉, 好比咱們緩存10000條數據,當數據小於10000時能夠隨意添加,當超過10000時就須要把新的數據添加進來,同時要把過時數據刪除,以確保咱們最 大緩存10000條,那怎麼肯定刪除哪條過時數據呢,採用LRU算法實現的話就是將最老的數據刪掉,廢話很少說,下面來講下Java版的LRU緩存實現 架構

Java裏面實現LRU緩存一般有兩種選擇,一種是使用LinkedHashMap,一種是本身設計數據結構,使用鏈表+HashMap app

LRU Cache的LinkedHashMap實現

LinkedHashMap自身已經實現了順序存儲,默認狀況下是按照元素的添加順序存儲,也能夠啓用按照訪問順序存儲,即最近讀取的數據放在最前 面,最先讀取的數據放在最後面,而後它還有一個判斷是否刪除最老數據的方法,默認是返回false,即不刪除數據,咱們使用LinkedHashMap實 現LRU緩存的方法就是對LinkedHashMap實現簡單的擴展,擴展方式有兩種,一種是inheritance,一種是delegation,具體 使用什麼方式看我的喜愛

複製代碼
//LinkedHashMap的一個構造函數,當參數accessOrder爲true時,即會按照訪問順序排序,最近訪問的放在最前,最先訪問的放在後面 public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder;
} //LinkedHashMap自帶的判斷是否刪除最老的元素方法,默認返回false,即不刪除老數據 //咱們要作的就是重寫這個方法,當知足必定條件時刪除老數據 protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false;
}
複製代碼

LRU緩存LinkedHashMap(inheritance)實現

採用inheritance方式實現比較簡單,並且實現了Map接口,在多線程環境使用時可使用 Collections.synchronizedMap()方法實現線程安全操做

複製代碼
package cn.lzrabbit.structure.lru; import java.util.LinkedHashMap; import java.util.Map; /** * Created by liuzhao on 14-5-15. */ public class LRUCache2<K, V> extends LinkedHashMap<K, V> { private final int MAX_CACHE_SIZE; public LRUCache2(int cacheSize) { super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
        MAX_CACHE_SIZE = cacheSize;
    }

    @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_CACHE_SIZE;
    }

    @Override public String toString() {
        StringBuilder sb = new StringBuilder(); for (Map.Entry<K, V> entry : entrySet()) {
            sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));
        } return sb.toString();
    }
}
複製代碼

 這樣算是比較標準的實現吧,實際使用中這樣寫仍是有些繁瑣,更實用的方法時像下面這樣寫,省去了單獨見一個類的麻煩

複製代碼
final int cacheSize = 100;
Map<String, String> map = new LinkedHashMap<String, String>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true) {
    @Override protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { return size() > cacheSize;
    }
};
複製代碼

 

LRU緩存LinkedHashMap(delegation)實現

delegation方式實現更加優雅一些,可是因爲沒有實現Map接口,因此線程同步就須要本身搞定了

複製代碼
package cn.lzrabbit.structure.lru; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; /** * Created by liuzhao on 14-5-13. */ public class LRUCache3<K, V> { private final int MAX_CACHE_SIZE; private final float DEFAULT_LOAD_FACTOR = 0.75f;
    LinkedHashMap<K, V> map; public LRUCache3(int cacheSize) {
        MAX_CACHE_SIZE = cacheSize; //根據cacheSize和加載因子計算hashmap的capactiy,+1確保當達到cacheSize上限時不會觸發hashmap的擴容, int capacity = (int) Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTOR) + 1;
        map = new LinkedHashMap(capacity, DEFAULT_LOAD_FACTOR, true) {
            @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_CACHE_SIZE;
            }
        };
    } public synchronized void put(K key, V value) {
        map.put(key, value);
    } public synchronized V get(K key) { return map.get(key);
    } public synchronized void remove(K key) {
        map.remove(key);
    } public synchronized Set<Map.Entry<K, V>> getAll() { return map.entrySet();
    } public synchronized int size() { return map.size();
    } public synchronized void clear() {
        map.clear();
    }

    @Override public String toString() {
        StringBuilder sb = new StringBuilder(); for (Map.Entry entry : map.entrySet()) {
            sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));
        } return sb.toString();
    }
}
複製代碼

 LRU Cache的鏈表+HashMap實現

 注:此實現爲非線程安全,若在多線程環境下使用須要在相關方法上添加synchronized以實現線程安全操做

複製代碼
package cn.lzrabbit.structure.lru; import java.util.HashMap; /** * Created by liuzhao on 14-5-12. */ public class LRUCache1<K, V> { private final int MAX_CACHE_SIZE; private Entry first; private Entry last; private HashMap<K, Entry<K, V>> hashMap; public LRUCache1(int cacheSize) {
        MAX_CACHE_SIZE = cacheSize;
        hashMap = new HashMap<K, Entry<K, V>>();
    } public void put(K key, V value) {
        Entry entry = getEntry(key); if (entry == null) { if (hashMap.size() >= MAX_CACHE_SIZE) {
                hashMap.remove(last.key);
                removeLast();
            }
            entry = new Entry();
            entry.key = key;
        }
        entry.value = value;
        moveToFirst(entry);
        hashMap.put(key, entry);
    } public V get(K key) {
        Entry<K, V> entry = getEntry(key); if (entry == null) return null;
        moveToFirst(entry); return entry.value;
    } public void remove(K key) {
        Entry entry = getEntry(key); if (entry != null) { if (entry.pre != null) entry.pre.next = entry.next; if (entry.next != null) entry.next.pre = entry.pre; if (entry == first) first = entry.next; if (entry == last) last = entry.pre;
        }
        hashMap.remove(key);
    } private void moveToFirst(Entry entry) { if (entry == first) return; if (entry.pre != null) entry.pre.next = entry.next; if (entry.next != null) entry.next.pre = entry.pre; if (entry == last) last = last.pre; if (first == null || last == null) {
            first = last = entry; return;
        }

        entry.next = first;
        first.pre = entry;
        first = entry;
        entry.pre = null;
    } private void removeLast() { if (last != null) {
            last = last.pre; if (last == null) first = null; else last.next = null;
        }
    } private Entry<K, V> getEntry(K key) { return hashMap.get(key);
    }

    @Override public String toString() {
        StringBuilder sb = new StringBuilder();
        Entry entry = first; while (entry != null) {
            sb.append(String.format("%s:%s ", entry.key, entry.value));
            entry = entry.next;
        } return sb.toString();
    } class Entry<K, V> { public Entry pre; public Entry next; public K key; public V value;
    }
}
複製代碼

LinkedHashMap的FIFO實現

FIFO是First Input First Output的縮寫,也就是常說的先入先出,默認狀況下LinkedHashMap就是按照添加順序保存,咱們只需重寫下removeEldestEntry方法便可輕鬆實現一個FIFO緩存,簡化版的實現代碼以下

複製代碼
final int cacheSize = 5;
LinkedHashMap<Integer, String> lru = new LinkedHashMap<Integer, String>() {
    @Override protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) { return size() > cacheSize;
    }
};
複製代碼

調用示例

測試代碼

View Code

運行結果

View Code

 

 

 

注:此文章屬懶惰的肥兔原創,版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接

若您以爲這篇文章還不錯請點擊下右下角的推薦,有了您的支持才能激發做者更大的寫做熱情,很是感謝。

若有問題,能夠經過lzrabbit@126.com聯繫我。

相關文章
相關標籤/搜索