001: /*
002: * MCS Media Computer Software Copyright (c) 2005 by MCS
003: * -------------------------------------- Created on 23.04.2005 by w.klaas
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
006: * use this file except in compliance with the License. You may obtain a copy of
007: * the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations under
015: * the License.
016: */
017: package de.mcs.utils;
018:
019: import java.lang.ref.Reference;
020: import java.lang.ref.ReferenceQueue;
021: import java.lang.ref.SoftReference;
022: import java.util.AbstractMap;
023: import java.util.AbstractSet;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.NoSuchElementException;
028: import java.util.Set;
029:
030: /**
031: * Implements a Map with only soft references backuped by a hashmap.
032: *
033: * @author w.klaas
034: */
035:
036: public class SoftHashMap extends AbstractMap {
037: /** the map to store. */
038: private Map m = null;
039:
040: /** the reference queue. */
041: private ReferenceQueue q = new ReferenceQueue();
042:
043: /**
044: * Constructs an empty <tt>HashMap</tt> with the default initial capacity
045: * (16) and the default load factor (0.75).
046: */
047: public SoftHashMap() {
048: m = new HashMap();
049: }
050:
051: /**
052: * Constructs an empty <tt>HashMap</tt> with the specified initial
053: * capacity and the default load factor (0.75).
054: *
055: * @param initialCapacity
056: * the initial capacity.
057: */
058: public SoftHashMap(final int initialCapacity) {
059: m = new HashMap(initialCapacity);
060: }
061:
062: /**
063: * Constructs an empty <tt>HashMap</tt> with the specified initial
064: * capacity and load factor.
065: *
066: * @param initialCapacity
067: * The initial capacity.
068: * @param loadFactor
069: * The load factor.
070: */
071: public SoftHashMap(final int initialCapacity, final float loadFactor) {
072: m = new HashMap(initialCapacity, loadFactor);
073: }
074:
075: /**
076: * Returns the value to which this map maps the specified key. Returns
077: * <tt>null</tt> if the map contains no mapping for this key. A return
078: * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
079: * map contains no mapping for the key; it's also possible that the map
080: * explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt>
081: * operation may be used to distinguish these two cases.
082: *
083: * <p>
084: * More formally, if this map contains a mapping from a key <tt>k</tt> to
085: * a value <tt>v</tt> such that <tt>(key==null ? k==null :
086: * key.equals(k))</tt>,
087: * then this method returns <tt>v</tt>; otherwise it returns
088: * <tt>null</tt>. (There can be at most one such mapping.)
089: *
090: * @param key
091: * key whose associated value is to be returned.
092: * @return the value to which this map maps the specified key, or
093: * <tt>null</tt> if the map contains no mapping for this key.
094: *
095: * @see #containsKey(Object)
096: */
097: public final Object get(final Object key) {
098: Object res = m.get(key);
099: return res == null ? null : ((Reference) res).get();
100: }
101:
102: /**
103: * @see Map#put(K, V)
104: */
105: public final Object put(final Object key, final Object value) {
106: processQueue();
107: Reference ref = new SoftEntry(key, value, q);
108: Object res = m.put(key, ref);
109: return res == null ? null : ((Reference) res).get();
110: }
111:
112: /** the entry set. */
113: private Set entrySet = null;
114:
115: /**
116: * getting the entry set.
117: *
118: * @return Set
119: */
120: public final Set entrySet() {
121: if (entrySet == null) {
122: entrySet = new EntrySet();
123: }
124: return entrySet;
125: }
126:
127: /**
128: * processing the softentry queue.
129: *
130: */
131: private void processQueue() {
132: Reference r;
133: while ((r = q.poll()) != null) {
134: SoftEntry e = (SoftEntry) r;
135: m.remove(e.key);
136: }
137: }
138:
139: /**
140: * @see java.util.Set#size()
141: */
142: public final int size() {
143: return entrySet().size();
144: }
145:
146: /**
147: * @see java.util.Set#remove(java.lang.Object)
148: */
149: public final Object remove(final Object key) {
150: processQueue();
151: Object res = m.remove(key);
152: return res == null ? null : ((Reference) res).get();
153: }
154:
155: /**
156: * @see java.util.Map#clear()
157: */
158: public final void clear() {
159: processQueue();
160: m.clear();
161: }
162:
163: /**
164: * this is the soft reference entry in the map.
165: *
166: * @author w.klaas
167: *
168: */
169: private static final class SoftEntry extends SoftReference {
170: /** neccessary so that freed objects can be removed. */
171: private Object key;
172:
173: /**
174: * private contructor to this entry.
175: *
176: * @param aKey
177: * the key for this entry
178: * @param value
179: * the value
180: * @param q
181: * the reference queue
182: */
183: private SoftEntry(final Object aKey, final Object value,
184: final ReferenceQueue q) {
185: super (value, q);
186: this .key = aKey;
187: }
188: }
189:
190: /**
191: * My set of entries.
192: *
193: * @author w.klaas
194: *
195: */
196: private class EntrySet extends AbstractSet {
197: /** the entry set to use. */
198: private Set entrySet = m.entrySet();
199:
200: /**
201: * @see java.util.Set#size()
202: */
203: public int size() {
204: int s = 0;
205: for (Iterator i = iterator(); i.hasNext(); i.next()) {
206: s++;
207: }
208: return s;
209: }
210:
211: /**
212: * @see java.util.Set#isEmpty()
213: */
214: public boolean isEmpty() {
215: return !(iterator().hasNext());
216: }
217:
218: /**
219: * @see java.util.Set#remove(java.lang.Object)
220: */
221: public boolean remove(final Object o) {
222: processQueue();
223: return super .remove(o);
224: }
225:
226: /**
227: * @see java.util.Set#iterator()
228: */
229: public Iterator iterator() {
230:
231: return new Iterator() {
232: private Iterator it = entrySet.iterator();
233:
234: private Entry next = null;
235:
236: private Object value = null;
237:
238: /*
239: * Strong reference to key, so that the GC will leave it alone
240: * as long as this Entry exists
241: */
242:
243: public boolean hasNext() {
244: while (it.hasNext()) {
245: final Entry e = (Entry) it.next();
246: SoftEntry se = (SoftEntry) e.getValue();
247: value = null;
248: if ((se != null)
249: && ((value = se.get()) == null)) {
250: /* Weak key has been cleared by GC */
251: continue;
252: }
253: next = new Map.Entry() {
254: public Object getKey() {
255: return e.getKey();
256: }
257:
258: public Object getValue() {
259: return value;
260: }
261:
262: public Object setValue(final Object v) {
263: Object res = value;
264: value = v;
265: e.setValue(new SoftEntry(e.getKey(),
266: value, q));
267: return res;
268: }
269:
270: public boolean equals(final Object x) {
271: if (!(x instanceof Map.Entry)) {
272: return false;
273: }
274: Map.Entry e = (Map.Entry) x;
275: Object key = getKey();
276: return key == null ? e.getKey() == null
277: : key.equals(e.getKey())
278: && value == null ? e
279: .getValue() == null
280: : value.equals(e
281: .getValue());
282: }
283:
284: public int hashCode() {
285: Object key = getKey();
286: return (((key == null) ? 0 : key
287: .hashCode()) ^ ((value == null) ? 0
288: : value.hashCode()));
289: }
290:
291: };
292: return true;
293: }
294: return false;
295: }
296:
297: public Object next() {
298: if ((next == null) && !hasNext()) {
299: throw new NoSuchElementException();
300: }
301: Entry e = next;
302: next = null;
303: return e;
304: }
305:
306: public void remove() {
307: it.remove();
308: }
309:
310: };
311: }
312: }
313: }
|