/** * Alipay.com Inc. * Copyright (c) 2004-2012 All Rights Reserved. */ package com.alipay.zdal.datasource.resource.util.collection; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.AbstractMap; import java.util.HashMap; 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 * * * @author ���� * @version $Id: WeakValueHashMap.java, v 0.1 2014-1-6 ����05:41:08 Exp $ */ public class WeakValueHashMap extends AbstractMap implements Map { private static class WeakValueRef extends WeakReference { public Object key; private WeakValueRef(Object key, Object val, ReferenceQueue q) { super(val, q); this.key = key; } private static WeakValueRef create(Object key, Object val, ReferenceQueue q) { if (val == null) return null; else return new WeakValueRef(key, val, q); } } @Override public Set entrySet() { processQueue(); return hash.entrySet(); } /* Hash table mapping WeakKeys to values */ private final Map hash; /* Reference queue for cleared WeakKeys */ private final ReferenceQueue queue = new ReferenceQueue(); /* Remove all invalidated entries from the map, that is, remove all entries whose values have been discarded. */ private void processQueue() { WeakValueRef ref; while ((ref = (WeakValueRef) queue.poll()) != null) { if (ref == (WeakValueRef) hash.get(ref.key)) { // only remove if it is the *exact* same WeakValueRef // hash.remove(ref.key); } } } /* -- Constructors -- */ /** * 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 WeakValueHashMap(int initialCapacity, float loadFactor) { hash = new HashMap(initialCapacity, loadFactor); } /** * 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 WeakValueHashMap(int initialCapacity) { hash = new HashMap(initialCapacity); } /** * Constructs a new, empty <code>WeakHashMap</code> with the default * initial capacity and the default load factor, which is * <code>0.75</code>. */ public WeakValueHashMap() { hash = new HashMap(); } /** * Constructs a new <code>WeakHashMap</code> with the same mappings as the * specified <tt>Map</tt>. 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 * <tt>0.75</tt>. * * @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); } /* -- Simple queries -- */ /** * 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 <code>true</code> if this map contains no key-value mappings. */ @Override public boolean isEmpty() { processQueue(); return hash.isEmpty(); } /** * 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 <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 Object get(Object key) { processQueue(); WeakReference ref = (WeakReference) hash.get(key); if (ref != null) return ref.get(); return null; } /** * 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 Object put(Object key, Object value) { processQueue(); Object rtn = hash.put(key, WeakValueRef.create(key, value, queue)); if (rtn != null) rtn = ((WeakReference) rtn).get(); return rtn; } /** * 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 Object remove(Object key) { processQueue(); return hash.remove(key); } /** * Removes all mappings from this map. */ @Override public void clear() { processQueue(); hash.clear(); } }