1 /***
2 * Created on 2005-02-11
3 * @author Philippe Lefebvre <philippe.lefebvre@gmail.com>
4 */
5 package net.sf.xtract.util;
6
7 import java.lang.ref.ReferenceQueue;
8 import java.lang.ref.SoftReference;
9 import java.util.AbstractMap;
10 import java.util.AbstractSet;
11 import java.util.Collection;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.Map;
15 import java.util.Set;
16
17 public class SoftHashMap<K,V> extends AbstractMap<K,V> {
18
19 /*** The internal HashMap that will hold the SoftReference. */
20 private final Map<K,Entry<K,V>> mHashMap;
21
22 /*** Reference queue for cleared SoftReference objects. */
23 private final ReferenceQueue<V> mQueue = new ReferenceQueue<V>();
24
25 public SoftHashMap() {
26 mHashMap = new HashMap<K, Entry<K,V>>();
27 }
28
29 public SoftHashMap(int pCapacity) {
30 mHashMap = new HashMap<K, Entry<K,V>>(pCapacity);
31 }
32
33 public SoftHashMap(int pCapacity, float pLoadFactor) {
34 mHashMap = new HashMap<K, Entry<K,V>>(pCapacity, pLoadFactor);
35 }
36
37 public V get(K pKey) {
38 V lResult = null;
39
40 SoftReference<V> lSoftRef = mHashMap.get(pKey);
41 if (lSoftRef != null) {
42
43
44
45 lResult = lSoftRef.get();
46 if (lResult == null) {
47
48
49 mHashMap.remove(pKey);
50 }
51 }
52 return lResult;
53 }
54
55 /***
56 * Here we go through the ReferenceQueue and remove garbage collected SoftValue objects from the
57 * HashMap by looking them up using the SoftValue.key data member.
58 */
59 private void processQueue() {
60 Entry lSoftValue = (Entry) mQueue.poll();
61 while (lSoftValue != null) {
62 mHashMap.remove(lSoftValue.mKey);
63 mQueue.poll();
64 }
65 }
66
67 /***
68 * Here we put the key, value pair into the HashMap using a SoftValue object.
69 */
70 public V put(K pKey, V pValue) {
71 processQueue();
72 mHashMap.put(pKey, new Entry<K,V>(pKey, pValue, mQueue));
73 return pValue;
74 }
75
76 public V remove(K pKey) {
77 processQueue();
78 Entry<K,V> lSoftValue = mHashMap.remove(pKey);
79 return lSoftValue.get();
80 }
81
82 public void clear() {
83 processQueue();
84 mHashMap.clear();
85 }
86
87 public int size() {
88 processQueue();
89 return mHashMap.size();
90 }
91
92 public Set<Map.Entry<K, V>> entrySet() {
93 return new EntrySet();
94 }
95
96 /***
97 * We define our own subclass of SoftReference which contains not only the value but also the key
98 * to make it easier to find the entry in the HashMap after it's been garbage collected.
99 */
100 private static class Entry<K,V> extends SoftReference<V> implements Map.Entry {
101 private final K mKey;
102
103 /***
104 * Did you know that an outer class can access private data members and methods of an inner
105 * class? I didn't know that! I thought it was only the inner class who could access the outer
106 * class's private information. An outer class can also access private members of an inner class
107 * inside its inner class.
108 */
109 private Entry(K pKey, V pValue, ReferenceQueue<V> pQueue) {
110 super(pValue, pQueue);
111 mKey = pKey;
112 }
113
114 /***
115 * @see java.util.Map.Entry#getKey()
116 */
117 public K getKey() {
118 return mKey;
119 }
120
121 /***
122 * @see java.util.Map.Entry#getValue()
123 */
124 public V getValue() {
125 return get();
126 }
127
128 /***
129 * @see java.util.Map.Entry#setValue(java.lang.Object)
130 */
131 public V setValue(Object pValue) {
132 throw new UnsupportedOperationException();
133 }
134 }
135
136 private class EntrySet extends AbstractSet {
137
138 private Collection<Entry<K,V>> mEntries = mHashMap.values();
139
140 /***
141 * @see java.util.AbstractCollection#iterator()
142 */
143 public Iterator<Entry<K,V>> iterator() {
144 return mEntries.iterator();
145 }
146
147 /***
148 * @see java.util.AbstractCollection#size()
149 */
150 public int size() {
151 return mEntries.size();
152 }
153
154 }
155
156 }