org.infinispan.commons.util.WeakValueHashMap Maven / Gradle / Ivy
package org.infinispan.commons.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* This Map will remove entries when the value in the map has been cleaned from
* garbage collection
*
* @param the key type
* @param the value type
* @author Bill Burke
* @author Adrian Brock
* @author Ales Justin
* @see
* JBoss Common Core source code for origins of this class
*/
public final class WeakValueHashMap extends java.util.AbstractMap {
/**
* Hash table mapping keys to ref values
*/
private Map> map;
/**
* Reference queue for cleared RefKeys
*/
private ReferenceQueue queue = new ReferenceQueue();
/**
* Constructs a new, empty WeakValueHashMap
with the given
* initial capacity and the given load factor.
*
* @param initialCapacity The initial capacity of the WeakValueHashMap
* @param loadFactor The load factor of the WeakValueHashMap
* @throws IllegalArgumentException If the initial capacity is less than
* zero, or if the load factor is
* nonpositive
*/
public WeakValueHashMap(int initialCapacity, float loadFactor) {
map = createMap(initialCapacity, loadFactor);
}
/**
* Constructs a new, empty WeakValueHashMap
with the given
* initial capacity and the default load factor, which is
* 0.75
.
*
* @param initialCapacity The initial capacity of the WeakValueHashMap
* @throws IllegalArgumentException If the initial capacity is less than
* zero
*/
public WeakValueHashMap(int initialCapacity) {
map = createMap(initialCapacity);
}
/**
* Constructs a new, empty WeakValueHashMap
with the default
* initial capacity and the default load factor, which is
* 0.75
.
*/
public WeakValueHashMap() {
map = createMap();
}
/**
* Constructs a new WeakValueHashMap
with the same mappings as
* the specified Map. The WeakValueHashMap
is created
* with an initial capacity of twice the number of mappings in the specified
* map or 11 (whichever is greater), and a default load factor, which is
* 0.75.
*
* @param t the map whose mappings are to be placed in this map.
* @since 1.3
*/
public WeakValueHashMap(Map t) {
this(Math.max(2 * t.size(), 11), 0.75f);
putAll(t);
}
/**
* Create new value ref instance.
*
* @param key the key
* @param value the value
* @param q the ref queue
* @return new value ref instance
*/
private ValueRef create(K key, V value, ReferenceQueue q) {
return WeakValueRef.create(key, value, q);
}
/**
* Create map.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @return new map instance
*/
private Map> createMap(int initialCapacity, float loadFactor) {
return new HashMap>(initialCapacity, loadFactor);
}
/**
* Create map.
*
* @param initialCapacity the initial capacity
* @return new map instance
*/
private Map> createMap(int initialCapacity) {
return new HashMap>(initialCapacity);
}
/**
* Create map.
*
* @return new map instance
*/
protected Map> createMap() {
return new HashMap>();
}
@Override
public int size() {
processQueue();
return map.size();
}
@Override
public boolean containsKey(Object key) {
processQueue();
return map.containsKey(key);
}
@Override
public V get(Object key) {
processQueue();
ValueRef ref = map.get(key);
if (ref != null)
return ref.get();
return null;
}
@Override
public V put(K key, V value) {
processQueue();
ValueRef ref = create(key, value, queue);
ValueRef result = map.put(key, ref);
if (result != null)
return result.get();
return null;
}
@Override
public V remove(Object key) {
processQueue();
ValueRef result = map.remove(key);
if (result != null)
return result.get();
return null;
}
@Override
public Set> entrySet() {
processQueue();
return new EntrySet();
}
@Override
public void clear() {
processQueue();
map.clear();
}
@Override
public String toString() {
return map.toString();
}
/**
* Remove all entries whose values have been discarded.
*/
@SuppressWarnings("unchecked")
private void processQueue() {
ValueRef ref = (ValueRef) queue.poll();
while (ref != null) {
// only remove if it is the *exact* same WeakValueRef
if (ref == map.get(ref.getKey()))
map.remove(ref.getKey());
ref = (ValueRef) queue.poll();
}
}
/**
* EntrySet.
*/
private class EntrySet extends AbstractSet> {
@Override
public Iterator> iterator() {
return new EntrySetIterator(map.entrySet().iterator());
}
@Override
public int size() {
return WeakValueHashMap.this.size();
}
}
/**
* EntrySet iterator
*/
private class EntrySetIterator implements Iterator> {
/**
* The delegate
*/
private Iterator>> delegate;
/**
* Create a new EntrySetIterator.
*
* @param delegate the delegate
*/
public EntrySetIterator(Iterator>> delegate) {
this.delegate = delegate;
}
public boolean hasNext() {
return delegate.hasNext();
}
public Entry next() {
Entry> next = delegate.next();
return next.getValue();
}
public void remove() {
throw new UnsupportedOperationException("remove");
}
}
/**
* Weak value ref.
*
* @param the key type
* @param the value type
* @author Bill Burke
* @author Adrian Brock
* @author Ales Justin
*/
private static class WeakValueRef
extends WeakReference implements ValueRef {
/**
* The key
*/
public K key;
/**
* Safely create a new WeakValueRef
*
* @param the key type
* @param the value type
* @param key the key
* @param val the value
* @param q the reference queue
* @return the reference or null if the value is null
*/
static WeakValueRef create(K key, V val, ReferenceQueue q) {
if (val == null)
return null;
else
return new WeakValueRef(key, val, q);
}
/**
* Create a new WeakValueRef.
*
* @param key the key
* @param val the value
* @param q the reference queue
*/
private WeakValueRef(K key, V val, ReferenceQueue q) {
super(val, q);
this.key = key;
}
public K getKey() {
return key;
}
public V getValue() {
return get();
}
public V setValue(V value) {
throw new UnsupportedOperationException("setValue");
}
@Override
public String toString() {
return String.valueOf(get());
}
}
public interface ValueRef extends Map.Entry {
/**
* Get underlying value.
*
* @return the value
*/
V get();
}
}