package net.wigle.wigleandroid.model; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; /** * try to be all things to all people. a remove-eldest feature, concurrent except during put, * which is usually single-threaded in this app anyway. * @param <K> key * @param <V> value */ public final class ConcurrentLinkedHashMap<K,V> { private final ConcurrentHashMap<K,V> map; private final LinkedBlockingQueue<K> queue; private int count = 0; private final int maxSize; private final Object WRITE_LOCK = new Object(); public ConcurrentLinkedHashMap() { this( Integer.MAX_VALUE ); } public ConcurrentLinkedHashMap( final int maxSize ) { map = new ConcurrentHashMap<>(); queue = new LinkedBlockingQueue<>(); this.maxSize = maxSize; } public V put( final K key, final V value ) { V previous; synchronized( WRITE_LOCK ) { previous = map.put(key, value); if ( previous == null ) { // new key! add to queue queue.add( key ); // check if this puts us over if ( count + 1 > maxSize ) { // remove eldest K eldest = queue.remove(); map.remove( eldest ); } else { // this doesn't put us over, just add to the count count++; } } } return previous; } public V get( K key ) { return map.get( key ); } /** * make sure this is only used for reading (we only use it for reading currently.) the map is concurrent safe, but it will bugger * our internal accounting for size() if the set is mutated */ public Set<Map.Entry<K,V>> entrySet() { return map.entrySet(); } public Collection<V> values() { return map.values(); } public boolean isEmpty() { return map.isEmpty(); } public int size() { return count; } public boolean isFull() { return count >= maxSize; } public int maxSize() { return maxSize; } }