All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cedarsoftware.util.LRUCache Maven / Gradle / Ivy

There is a newer version: 2.13.0
Show newest version
package com.cedarsoftware.util;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;

/**
 * This class provides a thread-safe Least Recently Used (LRU) cache API that will evict the least recently used items,
 * once a threshold is met.  It implements the Map interface for convenience. It is thread-safe via usage of
 * ReentrantReadWriteLock() around read and write APIs, including delegating to keySet(), entrySet(), and
 * values() and each of their iterators.
 * 

* @author John DeRegnaucourt ([email protected]) *
* Copyright (c) Cedar Software LLC *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* License *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class LRUCache implements Map { private final Map cache; private final transient ReadWriteLock lock = new ReentrantReadWriteLock(); private final static Object NO_ENTRY = new Object(); public LRUCache(int capacity) { cache = new LinkedHashMap(capacity, 0.75f, true) { protected boolean removeEldestEntry(Map.Entry eldest) { return size() > capacity; } }; } // Immutable APIs public boolean equals(Object obj) { return readOperation(() -> cache.equals(obj)); } public int hashCode() { return readOperation(cache::hashCode); } public String toString() { return readOperation(cache::toString); } public int size() { return readOperation(cache::size); } public boolean isEmpty() { return readOperation(cache::isEmpty); } public boolean containsKey(Object key) { return readOperation(() -> cache.containsKey(key)); } public boolean containsValue(Object value) { return readOperation(() -> cache.containsValue(value)); } public V get(Object key) { return readOperation(() -> cache.get(key)); } // Mutable APIs public V put(K key, V value) { return writeOperation(() -> cache.put(key, value)); } public void putAll(Map m) { writeOperation(() -> { cache.putAll(m); return null; }); } public V putIfAbsent(K key, V value) { return writeOperation(() -> cache.putIfAbsent(key, value)); } public V remove(Object key) { return writeOperation(() -> cache.remove(key)); } public void clear() { writeOperation(() -> { cache.clear(); return null; }); } public Set keySet() { return readOperation(() -> new Set() { public int size() { return readOperation(cache::size); } public boolean isEmpty() { return readOperation(cache::isEmpty); } public boolean contains(Object o) { return readOperation(() -> cache.containsKey(o)); } public Iterator iterator() { return new Iterator() { private final Iterator it = cache.keySet().iterator(); private K current = (K)NO_ENTRY; public boolean hasNext() { return readOperation(it::hasNext); } public K next() { return readOperation(() -> { current = it.next(); return current; }); } public void remove() { writeOperation(() -> { if (current == NO_ENTRY) { throw new IllegalStateException("Next not called or already removed"); } it.remove(); // Remove from the underlying map current = (K)NO_ENTRY; return null; }); } }; } public Object[] toArray() { return readOperation(() -> cache.keySet().toArray()); } public T[] toArray(T[] a) { return readOperation(() -> cache.keySet().toArray(a)); } public boolean add(K k) { throw new UnsupportedOperationException("add() not supported on .keySet() of a Map"); } public boolean remove(Object o) { return writeOperation(() -> cache.remove(o) != null); } public boolean containsAll(Collection c) { return readOperation(() -> cache.keySet().containsAll(c)); } public boolean addAll(Collection c) { throw new UnsupportedOperationException("addAll() not supported on .keySet() of a Map"); } public boolean retainAll(Collection c) { return writeOperation(() -> cache.keySet().retainAll(c)); } public boolean removeAll(Collection c) { return writeOperation(() -> cache.keySet().removeAll(c)); } public void clear() { writeOperation(() -> { cache.clear(); return null; }); } }); } public Collection values() { return readOperation(() -> new Collection() { public int size() { return readOperation(cache::size); } public boolean isEmpty() { return readOperation(cache::isEmpty); } public boolean contains(Object o) { return readOperation(() -> cache.containsValue(o)); } public Iterator iterator() { return new Iterator() { private final Iterator it = cache.values().iterator(); private V current = (V)NO_ENTRY; public boolean hasNext() { return readOperation(it::hasNext); } public V next() { return readOperation(() -> { current = it.next(); return current; }); } public void remove() { writeOperation(() -> { if (current == NO_ENTRY) { throw new IllegalStateException("Next not called or already removed"); } it.remove(); // Remove from the underlying map current = (V)NO_ENTRY; return null; }); } }; } public Object[] toArray() { return readOperation(() -> cache.values().toArray()); } public T[] toArray(T[] a) { return readOperation(() -> cache.values().toArray(a)); } public boolean add(V value) { throw new UnsupportedOperationException("add() not supported on values() of a Map"); } public boolean remove(Object o) { return writeOperation(() -> cache.values().remove(o)); } public boolean containsAll(Collection c) { return readOperation(() -> cache.values().containsAll(c)); } public boolean addAll(Collection c) { throw new UnsupportedOperationException("addAll() not supported on values() of a Map"); } public boolean removeAll(Collection c) { return writeOperation(() -> cache.values().removeAll(c)); } public boolean retainAll(Collection c) { return writeOperation(() -> cache.values().retainAll(c)); } public void clear() { writeOperation(() -> { cache.clear(); return null; }); } }); } public Set> entrySet() { return readOperation(() -> new Set>() { public int size() { return readOperation(cache::size); } public boolean isEmpty() { return readOperation(cache::isEmpty); } public boolean contains(Object o) { return readOperation(() -> cache.entrySet().contains(o)); } public Iterator> iterator() { return new Iterator>() { private final Iterator> it = cache.entrySet().iterator(); private Entry current = (Entry) NO_ENTRY; public boolean hasNext() { return readOperation(it::hasNext); } public Entry next() { return readOperation(() -> { current = it.next(); return current; }); } public void remove() { writeOperation(() -> { if (current == NO_ENTRY) { throw new IllegalStateException("Next not called or already removed"); } it.remove(); current = (Entry) NO_ENTRY; return null; }); } }; } public Object[] toArray() { return readOperation(() -> cache.entrySet().toArray()); } public T[] toArray(T[] a) { return readOperation(() -> cache.entrySet().toArray(a)); } public boolean add(Entry kvEntry) { throw new UnsupportedOperationException("add() not supported on entrySet() of a Map"); } public boolean remove(Object o) { return writeOperation(() -> cache.entrySet().remove(o)); } public boolean containsAll(Collection c) { return readOperation(() -> cache.entrySet().containsAll(c)); } public boolean addAll(Collection> c) { throw new UnsupportedOperationException("addAll() not supported on entrySet() of a Map"); } public boolean retainAll(Collection c) { return writeOperation(() -> cache.entrySet().retainAll(c)); } public boolean removeAll(Collection c) { return writeOperation(() -> cache.entrySet().removeAll(c)); } public void clear() { writeOperation(() -> { cache.clear(); return null; }); } }); } private T readOperation(Supplier operation) { lock.readLock().lock(); try { return operation.get(); } finally { lock.readLock().unlock(); } } private T writeOperation(Supplier operation) { lock.writeLock().lock(); try { return operation.get(); } finally { lock.writeLock().unlock(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy