View Javadoc

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      // We get the SoftReference represented by that key
40      SoftReference<V> lSoftRef = mHashMap.get(pKey);
41      if (lSoftRef != null) {
42        // From the SoftReference we get the value, which can be
43        // null if it was not in the map, or it was removed in
44        // the processQueue() method defined below
45        lResult = lSoftRef.get();
46        if (lResult == null) {
47          // If the value has been garbage collected, remove the
48          // entry from the HashMap.
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(); // throw out garbage collected values first
72      mHashMap.put(pKey, new Entry<K,V>(pKey, pValue, mQueue));
73      return pValue;
74    }
75  
76    public V remove(K pKey) {
77      processQueue(); // throw out garbage collected values first
78      Entry<K,V> lSoftValue = mHashMap.remove(pKey);
79      return lSoftValue.get();
80    }
81  
82    public void clear() {
83      processQueue(); // throw out garbage collected values
84      mHashMap.clear();
85    }
86  
87    public int size() {
88      processQueue(); // throw out garbage collected values first
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; // always make data member final
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 }