import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A simple LRU cache * * @author dennyliu * * @param <K> * @param <V> */ public class LRUCache<K extends Serializable, V> { /** * The map where the keys and values are stored in. */ private Map<K, Node> map = Collections.synchronizedMap(new HashMap<K, Node>()); private Node start = new Node(); // head node private Node end = new Node(); // the last node private int maxSize; private final Object lockObject = new Object(); /** * a simple linked node class * */ protected static class Node { public Node(Serializable key, Object value, long expires) { this.key = key; this.value = value; this.expires = expires; } public Node() { } public Serializable key; public Object value; public long expires; public Node previous; public Node next; } protected void removeNode(Node node) { synchronized (lockObject) { node.previous.next = node.next; node.next.previous = node.previous; } } protected void insertHead(Node node) { synchronized (lockObject) { node.previous = start; node.next = start.next; start.next.previous = node; start.next = node; } } protected void moveToHead(Node node) { synchronized (lockObject) { node.previous.next = node.next; node.next.previous = node.previous; node.previous = start; node.next = start.next; start.next.previous = node; start.next = node; } } /** * Constractor for LRUCache * * @param maxSize * How many objects can the cache hold */ public LRUCache(int maxSize) { this.maxSize = maxSize; this.start.next = end; this.start.previous = null; this.end.previous = start; this.end.next = null; } public static class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public void setKey(K key) { this.key = key; } public V getValue() { return value; } public void setValue(V value) { this.value = value; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Pair [key="); builder.append(key); builder.append(", value="); builder.append(value); builder.append("]"); return builder.toString(); } } public List<Pair<Serializable, Object>> getAll() { List<Pair<Serializable, Object>> list = new ArrayList<Pair<Serializable, Object>>(maxSize); synchronized (lockObject) { Node currentNode = start.next; while (currentNode != end) { list.add(new Pair<Serializable, Object>(currentNode.key, currentNode.value)); currentNode = currentNode.next; } } return list; } @SuppressWarnings("unchecked") public V get(K key) { Node currentNode = map.get(key); if (currentNode == null) { return null; } if (System.currentTimeMillis() > currentNode.expires) { map.remove(currentNode.key); removeNode(currentNode); return null; } if (currentNode != start.next) { moveToHead(currentNode); } return (V) currentNode.value; } public void put(K key, V obj) { put(key, obj, -1); } public void put(K key, V value, long validTime) { Node currentNode = map.get(key); if (currentNode != null) { currentNode.value = value; if (validTime > 0) { currentNode.expires = System.currentTimeMillis() + validTime; } else { currentNode.expires = Long.MAX_VALUE; } moveToHead(currentNode); return; } if (map.size() >= maxSize) { // remove the last node currentNode = end.previous; map.remove(currentNode.key); removeNode(currentNode); } long expires = 0; if (validTime > 0) { expires = System.currentTimeMillis() + validTime; } else { expires = Long.MAX_VALUE; } Node node = new Node(key, value, expires); insertHead(node); map.put(key, node); } public void remove(K key) { Node cur = map.get(key); if (cur == null) { return; } map.remove(key); removeNode(cur); } public int size() { return map.size(); } public static void main(String[] args) { LRUCache<String, String> cache = new LRUCache<String, String>(3); cache.put("111", "111"); cache.put("222", "222"); cache.put("333", "333"); System.out.println(cache.getAll()); cache.get("111"); System.out.println(cache.getAll()); cache.put("444", "444"); System.out.println(cache.getAll()); cache.put("555", "555"); System.out.println(cache.getAll()); } }