de.ck35.metricstore.util.LRUCache Maven / Gradle / Ivy
package de.ck35.metricstore.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
/**
* A cache which holds up to n elements. The element which has not been used for the longest time
* will be dropped when another element (n+1) is added.
*
* @param The cache key.
* @param The cached value.
*
* @author Christian Kaspari
* @since 1.0.0
*/
public class LRUCache implements Iterable {
private final Supplier maxCachedEntriesSupplier;
private final Map> cache;
private final Comparator>> comparator;
public LRUCache(int maxCachedEntries) {
this(Suppliers.ofInstance(maxCachedEntries));
}
public LRUCache(Supplier maxCachedEntriesSetting) {
this.maxCachedEntriesSupplier = maxCachedEntriesSetting;
this.cache = new HashMap<>();
this.comparator = new CacheEntryComparator<>();
}
public V get(K key) {
CacheEntry entry = cache.get(key);
if(entry == null) {
return null;
}
return entry.get();
}
public Iterable put(K key, V value) {
CacheEntry old = cache.put(key, new CacheEntry<>(value));
int maxCachedEntries = maxCachedEntriesSupplier.get();
if(cache.size() > maxCachedEntries) {
ImmutableList.Builder oldValues = ImmutableList.builder();
List>> list = new ArrayList<>(cache.entrySet());
Collections.sort(list, comparator);
int index = 0;
while(cache.size() > maxCachedEntries) {
oldValues.add(cache.remove(list.get(index++).getKey()).get());
}
return oldValues.build();
} else {
return old == null ? Collections.emptyList() : Collections.singleton(old.get());
}
}
public V remove(K key) {
CacheEntry cacheEntry = cache.remove(key);
return cacheEntry == null ? null : cacheEntry.get();
}
public int size() {
return cache.size();
}
public void clear() {
this.cache.clear();
}
@Override
public Iterator iterator() {
Function, V> function = Suppliers.supplierFunction();
return Collections2.transform(Collections.>unmodifiableCollection(cache.values()), function).iterator();
}
public static class CacheEntryComparator implements Comparator>> {
@Override
public int compare(Entry> o1, Entry> o2) {
return Long.compare(o1.getValue().getLastUse(), o2.getValue().getLastUse());
}
}
private static class CacheEntry implements Supplier {
private final V value;
private long lastUse;
public CacheEntry(V value) {
this.value = Objects.requireNonNull(value);
touch();
}
public final void touch() {
this.lastUse = System.nanoTime();
}
@Override
public V get() {
touch();
return value;
}
public long getLastUse() {
return lastUse;
}
}
}