Java中的LRU Cache实现与LinkedHashMap

实现方法:1、LinkedHashMap。2、双向链表+Map
cache写机制:Write-through、write-behind、write-around、cache-aside

1、LinkedHashMap

构造函数如下:

 public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

accessOrder默认是false,是按照加入顺序存储的,传入true即设置为按照最新访问顺序存储。
LinkedHashMap使用双向链表来存储LRU的访问顺序,使用父类即HashMap来的结构来保存数据,比如双向链表定义如下:

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;   //********
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

在HashMap的put实现 中,最后一行为

afterNodeInsertion(evict);

该函数由子类实现,在LinkedHashMap中该函数实现了移除最久未使用元素的功能:

void afterNodeInsertion(boolean evict) { // possibly remove eldest
    LinkedHashMap.Entry<K,V> first;
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}
2、双向链表+Map

可以看出LinkedHashMap就是使用双向链表+Map的形式实现LRU缓存的,那如果自己实现应该怎么写呢?
一般遇到的使用缓存有很多种场景,比如为了减少读数据库操作,下面的例子就是对应这种场景。还有比如重量级对象重用,这个就是cache没有的时候就new一个,同时更新cache。

package com.timer.database;
import com.timer.database.bean.DSimpleJob;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

public class LRUCache {

    private Map<String,DSimpleJob> cache = new HashMap<>();
    private LinkedList<String> list = new LinkedList<>();
    private int capcity;

    LRUCache(int capcity){
        this.capcity = capcity;
        Runtime.getRuntime().addShutdownHook(new Thread(this::flushCache));  //保证在JVM关闭前将cache的数据保存到数据库
    }

    public DSimpleJob get(String key) {
        if(cache.containsKey(key)) {
            resetToHead(key);
            return cache.get(key);
        } else {         //这里可以不实现直接返回null,由调用者自己从数据库读,然后自己调用set更新cache
            if(cache.size()<capcity) {
                resetToHead(key);
                return insertNew(key);
            }else {
                cacheFull();
                return  insertNew(key);
            }
        }
    }

    public void set(String key,DSimpleJob simpleJob) {
        if(cache.containsKey(key)) {
            resetToHead(key);
            return;
        }

        if(cache.size()<capcity) {
            resetToHead(key);
        }else {
            cacheFull();
            resetToHead(key);
        }
        cache.put(key,simpleJob);
    }

    private void resetToHead(String key) {
        list.remove(key);
        list.addFirst(key);
    }

    private void cacheFull() {
        String oldest = list.removeLast();
        cache.remove(oldest);
    }

    private DSimpleJob insertNew(String key) {
        //这里应该是从数据库读,然后更新,这里为了方便直接new一个了
        DSimpleJob index = new DSimpleJob();
        cache.put(key,index);
        return index;
    }

    public void setCapcity(int capcity) {
        if(this.capcity>capcity) {
            flushCache();   //将缓存数据更新到数据库
            decreaseCap();
        } else {
            this.capcity = capcity;
        }
    }

    public void clear() {
        flushCache();
        list.clear();
        cache.clear();
    }
    private void flushCache() {
        //....
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • LinkedHashMap 的实现原理 LinkedHashMap 概述 HashMap 是无序的,HashMap...
    小雪的笔记阅读 473评论 0 1
  • LinkedHashMap LinkedHashMap简介 public class LinkedHashMap<...
    史路比阅读 516评论 0 2
  • 正值开学季,学子们这两天都在为开学的事情忙活,以做足上学的准备。彼时,一场无硝烟的战争又将打响。这是一场持久...
    呼息阅读 228评论 0 1
  • 这节课我们主要学习了Layout的用法。LinearLayout又称作线性布局,是一种非常常用的布局,这个布局会将...
    三好市民YUHAI阅读 205评论 0 0
  • 药酒翻江海,头闷听雨昏。
    soar1997阅读 124评论 0 0