泛型实现 LRU 缓存

  • LRU(Least Recently Used,最近最久未使用)缓存思想:

(1)固定缓存大小,需要给缓存分配一个固定的大小
(2)每次读取缓存都会改变缓存的使用时间,将缓存的存在时间重新刷新
(3)需要在缓存满了后,将最近最久未使用的缓存删除,再添加最新的缓存

  • 实现思路:使用LinkedHashMap来实现LRU缓存。

LinkedHashMap的一个构造函数:

  1. public LinkedHashMap(int initialCapacity,
  2. float loadFactor,
  3. boolean accessOrder) {
  4. super(initialCapacity, loadFactor);
  5. this.accessOrder = accessOrder;
  6. }

传入的第三个参数:
accessOrder为true的时候,就按访问顺序对LinkedHashMap排序,
accessOrder为false的时候,就按插入顺序(默认情况)。
当把accessOrder设置为true后(按照访问顺序),就可以将最近访问的元素置于最前面。这样就可以满足上述的(2)。
LinkedHashMap是自动扩容的,当table数组中元素大于Capacity loadFactor的时候,就会自动进行两倍扩容。但是为了使缓存大小固定,就需要在初始化的时候传入容量大小*负载因子。为了使得到达设置缓存大小不会进行自动扩容,需要将初始化的大小进行计算再传入,将初始化大小设置为(缓存大小 / loadFactor) + 1,这样就可以在元素数目达到缓存大小时,也不会进行扩容了。这样就解决了上述的(1)。

  • 实现
    ```java public class LRUCache { private final int MAX_CACHE_SIZE; private final float DEFAULT_LOAD_FACTORY = 0.75f; private LinkedHashMap map;;

    public LRUCache(int cacheSize){

    1. MAX_CACHE_SIZE=cacheSize;
    2. int capacity=(int)Math.ceil(MAX_CACHE_SIZE/DEFAULT_LOAD_FACTORY)+1;
    3. //初始化大小设置为(缓存大小 / loadFactor) + 1,这样就可以在元素数目达到缓存大小时,也不会进行扩容
    4. map=new LinkedHashMap<K,V>(capacity,DEFAULT_LOAD_FACTORY,true){
    5. //true表示按照访问顺序
    6. @Override
    7. protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
    8. return size()>MAX_CACHE_SIZE;
    9. }
    10. };

    }

    //为了线程安全所有方法都是同步方法 public synchronized void put(K key,V value){

    1. map.put(key,value);

    }

    public synchronized V get(K key){

    1. return map.get(key);

    }

    public synchronized void remove(K key){

    1. map.remove(key);

    }

    public synchronized Set> getAll(){

    1. return map.entrySet();

    }

    @Override public String toString() {

    1. StringBuilder sb=new StringBuilder();
    2. for(Map.Entry<K,V> entry:map.entrySet()){
    3. sb.append(String.format("%s: %s\n",entry.getKey(),entry.getValue()));
    4. }
    5. return sb.toString();

    }

    public static void main(String[] args) {

    1. LRUCache<Integer,Integer> lru=new LRUCache<Integer, Integer>(5);
    2. //该缓存的容量是5
    3. lru.put(1, 1);
    4. lru.put(2, 2);
    5. lru.put(3, 3);
    6. System.out.println(lru);
    7. lru.get(1); //这里访问了 key=1的元素
    8. //按照访问顺序排序 --> key=1的元素是最新才访问的,所以key=2的元素是最近最久未访问的元素
    9. System.out.println(lru);
    10. lru.put(4,4);
    11. lru.put(5,5);
    12. lru.put(6,6);
    13. //容器的容量是5,当超过该容量时,会删除最近最久未访问的元素,也就是删除了key=2的元素
    14. System.out.println(lru);

    } } /* 输出结果 1: 1 2: 2 3: 3

2: 2 3: 3 1: 1

3: 3 1: 1 4: 4 5: 5 6: 6 */

  1. <a name="djg6y"></a>
  2. ### 泛型实现 FIFO 缓存
  3. - FIFO设计思路:FIFO就是先进先出,可以使用LinkedHashMap进行实现。<br />
  4. LinkedHashMap 的构造函数:
  5. ```java
  6. public LinkedHashMap(int initialCapacity,
  7. float loadFactor,
  8. boolean accessOrder) {
  9. super(initialCapacity, loadFactor);
  10. this.accessOrder = accessOrder;
  11. }

当第三个参数传入为false或者是默认的时候,就可以实现按插入顺序排序,就可以实现FIFO缓存了。

  1. public class FIFOCache<K,V> {
  2. private final int MAX_CACHE_SIZE;
  3. private final float DEFAULT_LOAD_FACTORY = 0.75f;
  4. private LinkedHashMap<K, V> map;
  5. public FIFOCache(int cacheSize) {
  6. this.MAX_CACHE_SIZE = cacheSize;
  7. int capacity = (int)Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTORY) + 1;
  8. map=new LinkedHashMap<K,V>(capacity,DEFAULT_LOAD_FACTORY,false){
  9. @Override
  10. protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
  11. return size() > MAX_CACHE_SIZE;
  12. }
  13. };
  14. }
  15. public synchronized void put(K key,V value){
  16. map.put(key,value);
  17. }
  18. public synchronized V get(K key){
  19. return map.get(key);
  20. }
  21. public synchronized void remove(K key){
  22. map.remove(key);
  23. }
  24. public synchronized Set<Map.Entry<K,V>> getAll(){
  25. return map.entrySet();
  26. }
  27. @Override
  28. public String toString() {
  29. StringBuilder sb=new StringBuilder();
  30. for(Map.Entry<K,V> entry:map.entrySet()){
  31. sb.append(String.format("%s: %s\n",entry.getKey(),entry.getValue()));
  32. }
  33. return sb.toString();
  34. }
  35. public static void main(String[] args) {
  36. //按照插入顺序
  37. FIFOCache<Integer,Integer> fifo=new FIFOCache<Integer, Integer>(5);
  38. fifo.put(1, 1);
  39. fifo.put(2, 2);
  40. fifo.put(3, 3);
  41. System.out.println(fifo);
  42. fifo.get(1);
  43. System.out.println(fifo);
  44. fifo.put(4,4);
  45. fifo.put(5,5);
  46. fifo.put(6,6);
  47. System.out.println(fifo);
  48. }
  49. }
  50. /* 输出结果
  51. 1: 1
  52. 2: 2
  53. 3: 3
  54. 1: 1
  55. 2: 2
  56. 3: 3
  57. 2: 2
  58. 3: 3
  59. 4: 4
  60. 5: 5
  61. 6: 6
  62. */