java LRU of Least Recently Used details and example code

  • 2020-05-19 04:49:37
  • OfStack

java LRU(Least Recently Used

LRU is Least Recently Used, "the least recently used", is the translation of LRU cache is to use this principle to achieve, in simple terms is 1 quantitative data cache, when more than set threshold is 1 that delete some outdated data, such as cache 10000 data, we can add when data is less than 10000, when more than 10000 will need to add new data, to delete expired data at the same time, to ensure that our biggest cache 10000 article, that how to determine what to delete the overdue data? The implementation of LRU algorithm is to delete the oldest data. Without further elaboration, let's talk about the Java version of LRU cache implementation

There are usually two options for implementing LRU caching in Java, one is to use LinkedHashMap, the other is to design your own data structure using linked list +HashMap

LRU Cache LinkedHashMap implementation

LinkedHashMap itself has achieved stored in order, by default are stored in order according to the element to add, can also enable the stored in access order, namely recently read data on the front, the earliest read data on the back, and it also has a judge whether to delete the old data method, the default is to return false, namely not to delete data, we use LinkedHashMap LRU cache method is to achieve LinkedHashMap realize simple extension, expand means has two kinds, 1 kind is inheritance, 1 kind is delegation, see individual be fond of specific use what way


//LinkedHashMap the 1 Constructors, when the argument accessOrder for true , that is, it will be sorted according to the access order, the most recent access in the first, the earliest access in the last 
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

//LinkedHashMap The built-in method to determine whether to delete the oldest element is returned by default false , that is, old data is not deleted 
// So what we're going to do is we're going to rewrite this method when we satisfy 1 Delete old data when setting a condition 
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

LRU cache LinkedHashMap(inheritance) implementation

The inheritance method is relatively simple to implement, and the Map interface is implemented. The Collections.synchronizedMap () method can be used to realize thread-safe operations in a multithreaded environment



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();
  }
}

This is a relatively standard implementation, the actual use of the writing is still a bit cumbersome, more practical methods like the following, save the trouble to see a single class



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 cache LinkedHashMap(delegation) implementation

The delegation approach is a bit more elegant, but since the Map interface is not implemented, thread synchronization is left to itself



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;
    // According to the cacheSize And load factor calculation hashmap the capactiy . +1 Make sure that when cacheSize It will not trigger when the limit is set hashmap The capacity of, 
    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 linked list +HashMap implementation

Note: this implementation is not thread safe. If used in a multithreaded environment, synchronized should be added to the relevant methods to realize thread safe operation



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;
  }
}

FIFO implementation of LinkedHashMap

FIFO is the abbreviation of First Input First Output, which is also known as "first in, first out". By default, LinkedHashMap is saved in the order of addition. We can easily implement an FIFO cache by rewriting the removeEldestEntry method



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

Invoke the sample


package cn.lzrabbit.structure.lru;

import cn.lzrabbit.ITest;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by liuzhao on 14-5-15.
 */
public class LRUCacheTest {

  public static void main(String[] args) throws Exception {
    System.out.println("start...");

    lruCache1();
    lruCache2();
    lruCache3();
    lruCache4();
   
    System.out.println("over...");
  }
 

 static  void lruCache1() {
    System.out.println();
    System.out.println("===========================LRU  Linked list implementation ===========================");
    LRUCache1<Integer, String> lru = new LRUCache1(5);
    lru.put(1, "11");
    lru.put(2, "11");
    lru.put(3, "11");
    lru.put(4, "11");
    lru.put(5, "11");
    System.out.println(lru.toString());
    lru.put(6, "66");
    lru.get(2);
    lru.put(7, "77");
    lru.get(4);
    System.out.println(lru.toString());
    System.out.println();
  }


static  <T> void lruCache2() {
    System.out.println();
    System.out.println("===========================LRU LinkedHashMap(inheritance) implementation ===========================");
    LRUCache2<Integer, String> lru = new LRUCache2(5);
    lru.put(1, "11");
    lru.put(2, "11");
    lru.put(3, "11");
    lru.put(4, "11");
    lru.put(5, "11");
    System.out.println(lru.toString());
    lru.put(6, "66");
    lru.get(2);
    lru.put(7, "77");
    lru.get(4);
    System.out.println(lru.toString());
    System.out.println();
  }

 static void lruCache3() {
    System.out.println();
    System.out.println("===========================LRU LinkedHashMap(delegation) implementation ===========================");
    LRUCache3<Integer, String> lru = new LRUCache3(5);
    lru.put(1, "11");
    lru.put(2, "11");
    lru.put(3, "11");
    lru.put(4, "11");
    lru.put(5, "11");
    System.out.println(lru.toString());
    lru.put(6, "66");
    lru.get(2);
    lru.put(7, "77");
    lru.get(4);
    System.out.println(lru.toString());
    System.out.println();
  }

 static void lruCache4() {
    System.out.println();
    System.out.println("===========================FIFO LinkedHashMap The default implementation ===========================");
    final int cacheSize = 5;
    LinkedHashMap<Integer, String> lru = new LinkedHashMap<Integer, String>() {
      @Override
      protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) {
        return size() > cacheSize;
      }
    };
    lru.put(1, "11");
    lru.put(2, "11");
    lru.put(3, "11");
    lru.put(4, "11");
    lru.put(5, "11");
    System.out.println(lru.toString());
    lru.put(6, "66");
    lru.get(2);
    lru.put(7, "77");
    lru.get(4);
    System.out.println(lru.toString());
    System.out.println();
  }

}

The results



"C:\Program Files (x86)\Java\jdk1.6.0_10\bin\java" -Didea.launcher.port=7535 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA 13.0.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\charsets.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\deploy.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\javaws.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\jce.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\jsse.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\management-agent.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\plugin.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\resources.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\rt.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\dnsns.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\localedata.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\sunjce_provider.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\sunmscapi.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\sunpkcs11.jar;D:\SVN\projects\Java\Java.Algorithm\target\test-classes;D:\SVN\projects\Java\Java.Algorithm\target\classes;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 13.0.2\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain Main
start...

===========================LRU  Linked list implementation ===========================
5:11 4:11 3:11 2:11 1:11 
4:11 7:77 2:11 6:66 5:11 


===========================LRU LinkedHashMap(inheritance) implementation ===========================
1:11 2:11 3:11 4:11 5:11 
5:11 6:66 2:11 7:77 4:11 


===========================LRU LinkedHashMap(delegation) implementation ===========================
1:11 2:11 3:11 4:11 5:11 
5:11 6:66 2:11 7:77 4:11 


===========================FIFO LinkedHashMap The default implementation ===========================
{1=11, 2=11, 3=11, 4=11, 5=11}
{3=11, 4=11, 5=11, 6=66, 7=77}

over...

Process finished with exit code 0

Thank you for reading, I hope to help you, thank you for your support of this site!


Related articles: