1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16 package javassist.scopedpool; 17 18 import java.lang.ref.ReferenceQueue; 19 import java.lang.ref.SoftReference; 20 import java.util.AbstractMap; 21 import java.util.HashMap; 22 import java.util.Map; 23 import java.util.Set; 24 25 /** 26 * This Map will remove entries when the value in the map has been cleaned from 27 * garbage collection 28 * 29 * @version <tt>$Revision: 1.4 $</tt> 30 * @author <a href="mailto:bill@jboss.org">Bill Burke</a> 31 */ 32 public class SoftValueHashMap extends AbstractMap implements Map { 33 private static class SoftValueRef extends SoftReference { 34 public Object key; 35 SoftValueRef(Object key, Object val, ReferenceQueue q)36 private SoftValueRef(Object key, Object val, ReferenceQueue q) { 37 super(val, q); 38 this.key = key; 39 } 40 create(Object key, Object val, ReferenceQueue q)41 private static SoftValueRef create(Object key, Object val, 42 ReferenceQueue q) { 43 if (val == null) 44 return null; 45 else 46 return new SoftValueRef(key, val, q); 47 } 48 49 } 50 51 /** 52 * Returns a set of the mappings contained in this hash table. 53 */ entrySet()54 public Set entrySet() { 55 processQueue(); 56 return hash.entrySet(); 57 } 58 59 /* Hash table mapping WeakKeys to values */ 60 private Map hash; 61 62 /* Reference queue for cleared WeakKeys */ 63 private ReferenceQueue queue = new ReferenceQueue(); 64 65 /* 66 * Remove all invalidated entries from the map, that is, remove all entries 67 * whose values have been discarded. 68 */ processQueue()69 private void processQueue() { 70 SoftValueRef ref; 71 while ((ref = (SoftValueRef)queue.poll()) != null) { 72 if (ref == (SoftValueRef)hash.get(ref.key)) { 73 // only remove if it is the *exact* same WeakValueRef 74 // 75 hash.remove(ref.key); 76 } 77 } 78 } 79 80 /* -- Constructors -- */ 81 82 /** 83 * Constructs a new, empty <code>WeakHashMap</code> with the given initial 84 * capacity and the given load factor. 85 * 86 * @param initialCapacity 87 * The initial capacity of the <code>WeakHashMap</code> 88 * 89 * @param loadFactor 90 * The load factor of the <code>WeakHashMap</code> 91 * 92 * @throws IllegalArgumentException 93 * If the initial capacity is less than zero, or if the load 94 * factor is nonpositive 95 */ SoftValueHashMap(int initialCapacity, float loadFactor)96 public SoftValueHashMap(int initialCapacity, float loadFactor) { 97 hash = new HashMap(initialCapacity, loadFactor); 98 } 99 100 /** 101 * Constructs a new, empty <code>WeakHashMap</code> with the given initial 102 * capacity and the default load factor, which is <code>0.75</code>. 103 * 104 * @param initialCapacity 105 * The initial capacity of the <code>WeakHashMap</code> 106 * 107 * @throws IllegalArgumentException 108 * If the initial capacity is less than zero 109 */ SoftValueHashMap(int initialCapacity)110 public SoftValueHashMap(int initialCapacity) { 111 hash = new HashMap(initialCapacity); 112 } 113 114 /** 115 * Constructs a new, empty <code>WeakHashMap</code> with the default 116 * initial capacity and the default load factor, which is <code>0.75</code>. 117 */ SoftValueHashMap()118 public SoftValueHashMap() { 119 hash = new HashMap(); 120 } 121 122 /** 123 * Constructs a new <code>WeakHashMap</code> with the same mappings as the 124 * specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with 125 * an initial capacity of twice the number of mappings in the specified map 126 * or 11 (whichever is greater), and a default load factor, which is 127 * <tt>0.75</tt>. 128 * 129 * @param t the map whose mappings are to be placed in this map. 130 */ SoftValueHashMap(Map t)131 public SoftValueHashMap(Map t) { 132 this(Math.max(2 * t.size(), 11), 0.75f); 133 putAll(t); 134 } 135 136 /* -- Simple queries -- */ 137 138 /** 139 * Returns the number of key-value mappings in this map. <strong>Note:</strong> 140 * <em>In contrast with most implementations of the 141 * <code>Map</code> interface, the time required by this operation is 142 * linear in the size of the map.</em> 143 */ size()144 public int size() { 145 processQueue(); 146 return hash.size(); 147 } 148 149 /** 150 * Returns <code>true</code> if this map contains no key-value mappings. 151 */ isEmpty()152 public boolean isEmpty() { 153 processQueue(); 154 return hash.isEmpty(); 155 } 156 157 /** 158 * Returns <code>true</code> if this map contains a mapping for the 159 * specified key. 160 * 161 * @param key 162 * The key whose presence in this map is to be tested. 163 */ containsKey(Object key)164 public boolean containsKey(Object key) { 165 processQueue(); 166 return hash.containsKey(key); 167 } 168 169 /* -- Lookup and modification operations -- */ 170 171 /** 172 * Returns the value to which this map maps the specified <code>key</code>. 173 * If this map does not contain a value for this key, then return 174 * <code>null</code>. 175 * 176 * @param key 177 * The key whose associated value, if any, is to be returned. 178 */ get(Object key)179 public Object get(Object key) { 180 processQueue(); 181 SoftReference ref = (SoftReference)hash.get(key); 182 if (ref != null) 183 return ref.get(); 184 return null; 185 } 186 187 /** 188 * Updates this map so that the given <code>key</code> maps to the given 189 * <code>value</code>. If the map previously contained a mapping for 190 * <code>key</code> then that mapping is replaced and the previous value 191 * is returned. 192 * 193 * @param key 194 * The key that is to be mapped to the given <code>value</code> 195 * @param value 196 * The value to which the given <code>key</code> is to be 197 * mapped 198 * 199 * @return The previous value to which this key was mapped, or 200 * <code>null</code> if if there was no mapping for the key 201 */ put(Object key, Object value)202 public Object put(Object key, Object value) { 203 processQueue(); 204 Object rtn = hash.put(key, SoftValueRef.create(key, value, queue)); 205 if (rtn != null) 206 rtn = ((SoftReference)rtn).get(); 207 return rtn; 208 } 209 210 /** 211 * Removes the mapping for the given <code>key</code> from this map, if 212 * present. 213 * 214 * @param key 215 * The key whose mapping is to be removed. 216 * 217 * @return The value to which this key was mapped, or <code>null</code> if 218 * there was no mapping for the key. 219 */ remove(Object key)220 public Object remove(Object key) { 221 processQueue(); 222 return hash.remove(key); 223 } 224 225 /** 226 * Removes all mappings from this map. 227 */ clear()228 public void clear() { 229 processQueue(); 230 hash.clear(); 231 } 232 } 233