/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package javassist.scopedpool;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

This Map will remove entries when the value in the map has been cleaned from garbage collection
Author:Bill Burke
Version:$Revision: 1.4 $
/** * This Map will remove entries when the value in the map has been cleaned from * garbage collection * * @version <code>$Revision: 1.4 $</code> * @author <a href="mailto:bill@jboss.org">Bill Burke</a> */
public class SoftValueHashMap<K,V> implements Map<K,V> { private static class SoftValueRef<K,V> extends SoftReference<V> { public K key; private SoftValueRef(K key, V val, ReferenceQueue<V> q) { super(val, q); this.key = key; } private static <K,V> SoftValueRef<K,V> create( K key, V val, ReferenceQueue<V> q) { if (val == null) return null; else return new SoftValueRef<K,V>(key, val, q); } }
Returns a set of the mappings contained in this hash table.
/** * Returns a set of the mappings contained in this hash table. */
@Override public Set<Map.Entry<K, V>> entrySet() { processQueue(); Set<Entry<K,V>> ret = new HashSet<Entry<K,V>>(); for (Entry<K,SoftValueRef<K,V>> e:hash.entrySet()) ret.add(new SimpleImmutableEntry<K,V> ( e.getKey(), e.getValue().get())); return ret; } /* Hash table mapping WeakKeys to values */ private Map<K,SoftValueRef<K,V>> hash; /* Reference queue for cleared WeakKeys */ private ReferenceQueue<V> queue = new ReferenceQueue<V>(); /* * Remove all invalidated entries from the map, that is, remove all entries * whose values have been discarded. */ private void processQueue() { Object ref; if (!hash.isEmpty()) while ((ref = queue.poll()) != null) if (ref instanceof SoftValueRef) { @SuppressWarnings("rawtypes") SoftValueRef que =(SoftValueRef) ref; if (ref == hash.get(que.key)) // only remove if it is the *exact* same SoftValueRef hash.remove(que.key); } } /* -- Constructors -- */
Constructs a new, empty WeakHashMap with the given initial capacity and the given load factor.
Params:
  • initialCapacity – The initial capacity of the WeakHashMap
  • loadFactor – The load factor of the WeakHashMap
Throws:
/** * Constructs a new, empty <code>WeakHashMap</code> with the given initial * capacity and the given load factor. * * @param initialCapacity * The initial capacity of the <code>WeakHashMap</code> * * @param loadFactor * The load factor of the <code>WeakHashMap</code> * * @throws IllegalArgumentException * If the initial capacity is less than zero, or if the load * factor is nonpositive */
public SoftValueHashMap(int initialCapacity, float loadFactor) { hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(initialCapacity, loadFactor); }
Constructs a new, empty WeakHashMap with the given initial capacity and the default load factor, which is 0.75.
Params:
  • initialCapacity – The initial capacity of the WeakHashMap
Throws:
/** * Constructs a new, empty <code>WeakHashMap</code> with the given initial * capacity and the default load factor, which is <code>0.75</code>. * * @param initialCapacity * The initial capacity of the <code>WeakHashMap</code> * * @throws IllegalArgumentException * If the initial capacity is less than zero */
public SoftValueHashMap(int initialCapacity) { hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(initialCapacity); }
Constructs a new, empty WeakHashMap with the default initial capacity and the default load factor, which is 0.75.
/** * Constructs a new, empty <code>WeakHashMap</code> with the default * initial capacity and the default load factor, which is <code>0.75</code>. */
public SoftValueHashMap() { hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(); }
Constructs a new WeakHashMap with the same mappings as the specified Map. The WeakHashMap 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.
Params:
  • t – the map whose mappings are to be placed in this map.
/** * Constructs a new <code>WeakHashMap</code> with the same mappings as the * specified <code>Map</code>. The <code>WeakHashMap</code> 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 * <code>0.75</code>. * * @param t the map whose mappings are to be placed in this map. */
public SoftValueHashMap(Map<K,V> t) { this(Math.max(2 * t.size(), 11), 0.75f); putAll(t); } /* -- Simple queries -- */
Returns the number of key-value mappings in this map. Note: In contrast with most implementations of the Map interface, the time required by this operation is linear in the size of the map.
/** * Returns the number of key-value mappings in this map. <strong>Note:</strong> * <em>In contrast with most implementations of the * <code>Map</code> interface, the time required by this operation is * linear in the size of the map.</em> */
@Override public int size() { processQueue(); return hash.size(); }
Returns true if this map contains no key-value mappings.
/** * Returns <code>true</code> if this map contains no key-value mappings. */
@Override public boolean isEmpty() { processQueue(); return hash.isEmpty(); }
Returns true if this map contains a mapping for the specified key.
Params:
  • key – The key whose presence in this map is to be tested.
/** * Returns <code>true</code> if this map contains a mapping for the * specified key. * * @param key * The key whose presence in this map is to be tested. */
@Override public boolean containsKey(Object key) { processQueue(); return hash.containsKey(key); } /* -- Lookup and modification operations -- */
Returns the value to which this map maps the specified key. If this map does not contain a value for this key, then return null.
Params:
  • key – The key whose associated value, if any, is to be returned.
/** * Returns the value to which this map maps the specified <code>key</code>. * If this map does not contain a value for this key, then return * <code>null</code>. * * @param key * The key whose associated value, if any, is to be returned. */
@Override public V get(Object key) { processQueue(); return valueOrNull(hash.get(key)); }
Updates this map so that the given key maps to the given value. If the map previously contained a mapping for key then that mapping is replaced and the previous value is returned.
Params:
  • key – The key that is to be mapped to the given value
  • value – The value to which the given key is to be mapped
Returns:The previous value to which this key was mapped, or null if if there was no mapping for the key
/** * Updates this map so that the given <code>key</code> maps to the given * <code>value</code>. If the map previously contained a mapping for * <code>key</code> then that mapping is replaced and the previous value * is returned. * * @param key * The key that is to be mapped to the given <code>value</code> * @param value * The value to which the given <code>key</code> is to be * mapped * * @return The previous value to which this key was mapped, or * <code>null</code> if if there was no mapping for the key */
@Override public V put(K key, V value) { processQueue(); return valueOrNull(hash.put(key, SoftValueRef.create(key, value, queue))); }
Removes the mapping for the given key from this map, if present.
Params:
  • key – The key whose mapping is to be removed.
Returns:The value to which this key was mapped, or null if there was no mapping for the key.
/** * Removes the mapping for the given <code>key</code> from this map, if * present. * * @param key * The key whose mapping is to be removed. * * @return The value to which this key was mapped, or <code>null</code> if * there was no mapping for the key. */
@Override public V remove(Object key) { processQueue(); return valueOrNull(hash.remove(key)); }
Removes all mappings from this map.
/** * Removes all mappings from this map. */
@Override public void clear() { processQueue(); hash.clear(); } /* * Check whether the supplied value exists. * @param Object the value to compare. * @return true if it was found or null. */ @Override public boolean containsValue(Object arg0) { processQueue(); if (null == arg0) return false; for (SoftValueRef<K,V> e:hash.values()) if (null != e && arg0.equals(e.get())) return true; return false; } /* {@inheritDoc} */ @Override public Set<K> keySet() { processQueue(); return hash.keySet(); } /* {@inheritDoc} */ @Override public void putAll(Map<? extends K,? extends V> arg0) { processQueue(); for (K key:arg0.keySet()) put(key, arg0.get(key)); } /* {@inheritDoc} */ @Override public Collection<V> values() { processQueue(); List<V> ret = new ArrayList<V>(); for (SoftValueRef<K,V> e:hash.values()) ret.add(e.get()); return ret; } private V valueOrNull(SoftValueRef<K,V> rtn) { if (null == rtn) return null; return rtn.get(); } }