You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SoftValueHashMap.java 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999- 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. * or the Apache License Version 2.0.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. */
  16. package javassist.scopedpool;
  17. import java.lang.ref.ReferenceQueue;
  18. import java.lang.ref.SoftReference;
  19. import java.util.AbstractMap.SimpleImmutableEntry;
  20. import java.util.ArrayList;
  21. import java.util.Collection;
  22. import java.util.HashSet;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Set;
  26. import java.util.concurrent.ConcurrentHashMap;
  27. /**
  28. * This Map will remove entries when the value in the map has been cleaned from
  29. * garbage collection
  30. *
  31. * @version <code>$Revision: 1.4 $</code>
  32. * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
  33. */
  34. public class SoftValueHashMap<K,V> implements Map<K,V> {
  35. private static class SoftValueRef<K,V> extends SoftReference<V> {
  36. public K key;
  37. private SoftValueRef(K key, V val, ReferenceQueue<V> q) {
  38. super(val, q);
  39. this.key = key;
  40. }
  41. private static <K,V> SoftValueRef<K,V> create(
  42. K key, V val, ReferenceQueue<V> q) {
  43. if (val == null)
  44. return null;
  45. else
  46. return new SoftValueRef<K,V>(key, val, q);
  47. }
  48. }
  49. /**
  50. * Returns a set of the mappings contained in this hash table.
  51. */
  52. @Override
  53. public Set<Map.Entry<K, V>> entrySet() {
  54. processQueue();
  55. Set<Entry<K,V>> ret = new HashSet<Entry<K,V>>();
  56. for (Entry<K,SoftValueRef<K,V>> e:hash.entrySet())
  57. ret.add(new SimpleImmutableEntry<K,V> (
  58. e.getKey(), e.getValue().get()));
  59. return ret;
  60. }
  61. /* Hash table mapping WeakKeys to values */
  62. private Map<K,SoftValueRef<K,V>> hash;
  63. /* Reference queue for cleared WeakKeys */
  64. private ReferenceQueue<V> queue = new ReferenceQueue<V>();
  65. /*
  66. * Remove all invalidated entries from the map, that is, remove all entries
  67. * whose values have been discarded.
  68. */
  69. private void processQueue() {
  70. Object ref;
  71. if (!hash.isEmpty())
  72. while ((ref = queue.poll()) != null)
  73. if (ref instanceof SoftValueRef) {
  74. @SuppressWarnings("rawtypes")
  75. SoftValueRef que =(SoftValueRef) ref;
  76. if (ref == hash.get(que.key))
  77. // only remove if it is the *exact* same SoftValueRef
  78. hash.remove(que.key);
  79. }
  80. }
  81. /* -- Constructors -- */
  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. */
  96. public SoftValueHashMap(int initialCapacity, float loadFactor) {
  97. hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(initialCapacity, loadFactor);
  98. }
  99. /**
  100. * Constructs a new, empty <code>WeakHashMap</code> with the given initial
  101. * capacity and the default load factor, which is <code>0.75</code>.
  102. *
  103. * @param initialCapacity
  104. * The initial capacity of the <code>WeakHashMap</code>
  105. *
  106. * @throws IllegalArgumentException
  107. * If the initial capacity is less than zero
  108. */
  109. public SoftValueHashMap(int initialCapacity) {
  110. hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(initialCapacity);
  111. }
  112. /**
  113. * Constructs a new, empty <code>WeakHashMap</code> with the default
  114. * initial capacity and the default load factor, which is <code>0.75</code>.
  115. */
  116. public SoftValueHashMap() {
  117. hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>();
  118. }
  119. /**
  120. * Constructs a new <code>WeakHashMap</code> with the same mappings as the
  121. * specified <code>Map</code>. The <code>WeakHashMap</code> is created with
  122. * an initial capacity of twice the number of mappings in the specified map
  123. * or 11 (whichever is greater), and a default load factor, which is
  124. * <code>0.75</code>.
  125. *
  126. * @param t the map whose mappings are to be placed in this map.
  127. */
  128. public SoftValueHashMap(Map<K,V> t) {
  129. this(Math.max(2 * t.size(), 11), 0.75f);
  130. putAll(t);
  131. }
  132. /* -- Simple queries -- */
  133. /**
  134. * Returns the number of key-value mappings in this map. <strong>Note:</strong>
  135. * <em>In contrast with most implementations of the
  136. * <code>Map</code> interface, the time required by this operation is
  137. * linear in the size of the map.</em>
  138. */
  139. @Override
  140. public int size() {
  141. processQueue();
  142. return hash.size();
  143. }
  144. /**
  145. * Returns <code>true</code> if this map contains no key-value mappings.
  146. */
  147. @Override
  148. public boolean isEmpty() {
  149. processQueue();
  150. return hash.isEmpty();
  151. }
  152. /**
  153. * Returns <code>true</code> if this map contains a mapping for the
  154. * specified key.
  155. *
  156. * @param key
  157. * The key whose presence in this map is to be tested.
  158. */
  159. @Override
  160. public boolean containsKey(Object key) {
  161. processQueue();
  162. return hash.containsKey(key);
  163. }
  164. /* -- Lookup and modification operations -- */
  165. /**
  166. * Returns the value to which this map maps the specified <code>key</code>.
  167. * If this map does not contain a value for this key, then return
  168. * <code>null</code>.
  169. *
  170. * @param key
  171. * The key whose associated value, if any, is to be returned.
  172. */
  173. @Override
  174. public V get(Object key) {
  175. processQueue();
  176. return valueOrNull(hash.get(key));
  177. }
  178. /**
  179. * Updates this map so that the given <code>key</code> maps to the given
  180. * <code>value</code>. If the map previously contained a mapping for
  181. * <code>key</code> then that mapping is replaced and the previous value
  182. * is returned.
  183. *
  184. * @param key
  185. * The key that is to be mapped to the given <code>value</code>
  186. * @param value
  187. * The value to which the given <code>key</code> is to be
  188. * mapped
  189. *
  190. * @return The previous value to which this key was mapped, or
  191. * <code>null</code> if if there was no mapping for the key
  192. */
  193. @Override
  194. public V put(K key, V value) {
  195. processQueue();
  196. return valueOrNull(hash.put(key, SoftValueRef.create(key, value, queue)));
  197. }
  198. /**
  199. * Removes the mapping for the given <code>key</code> from this map, if
  200. * present.
  201. *
  202. * @param key
  203. * The key whose mapping is to be removed.
  204. *
  205. * @return The value to which this key was mapped, or <code>null</code> if
  206. * there was no mapping for the key.
  207. */
  208. @Override
  209. public V remove(Object key) {
  210. processQueue();
  211. return valueOrNull(hash.remove(key));
  212. }
  213. /**
  214. * Removes all mappings from this map.
  215. */
  216. @Override
  217. public void clear() {
  218. processQueue();
  219. hash.clear();
  220. }
  221. /*
  222. * Check whether the supplied value exists.
  223. * @param Object the value to compare.
  224. * @return true if it was found or null.
  225. */
  226. @Override
  227. public boolean containsValue(Object arg0) {
  228. processQueue();
  229. if (null == arg0)
  230. return false;
  231. for (SoftValueRef<K,V> e:hash.values())
  232. if (null != e && arg0.equals(e.get()))
  233. return true;
  234. return false;
  235. }
  236. /* {@inheritDoc} */
  237. @Override
  238. public Set<K> keySet() {
  239. processQueue();
  240. return hash.keySet();
  241. }
  242. /* {@inheritDoc} */
  243. @Override
  244. public void putAll(Map<? extends K,? extends V> arg0) {
  245. processQueue();
  246. for (K key:arg0.keySet())
  247. put(key, arg0.get(key));
  248. }
  249. /* {@inheritDoc} */
  250. @Override
  251. public Collection<V> values() {
  252. processQueue();
  253. List<V> ret = new ArrayList<V>();
  254. for (SoftValueRef<K,V> e:hash.values())
  255. ret.add(e.get());
  256. return ret;
  257. }
  258. private V valueOrNull(SoftValueRef<K,V> rtn) {
  259. if (null == rtn)
  260. return null;
  261. return rtn.get();
  262. }
  263. }