001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of 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,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.collections;
018:
019: import java.io.IOException;
020: import java.io.ObjectInputStream;
021: import java.io.ObjectOutputStream;
022: import java.lang.ref.Reference;
023: import java.lang.ref.ReferenceQueue;
024: import java.lang.ref.SoftReference;
025: import java.lang.ref.WeakReference;
026: import java.util.AbstractCollection;
027: import java.util.AbstractMap;
028: import java.util.AbstractSet;
029: import java.util.ArrayList;
030: import java.util.Arrays;
031: import java.util.Collection;
032: import java.util.ConcurrentModificationException;
033: import java.util.Iterator;
034: import java.util.Map;
035: import java.util.NoSuchElementException;
036: import java.util.Set;
037:
038: import org.apache.commons.collections.keyvalue.DefaultMapEntry;
039:
040: /**
041: * Hash-based {@link Map} implementation that allows
042: * mappings to be removed by the garbage collector.<p>
043: *
044: * When you construct a <code>ReferenceMap</code>, you can
045: * specify what kind of references are used to store the
046: * map's keys and values. If non-hard references are
047: * used, then the garbage collector can remove mappings
048: * if a key or value becomes unreachable, or if the
049: * JVM's memory is running low. For information on how
050: * the different reference types behave, see
051: * {@link Reference}.<p>
052: *
053: * Different types of references can be specified for keys
054: * and values. The keys can be configured to be weak but
055: * the values hard, in which case this class will behave
056: * like a <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
057: * <code>WeakHashMap</code></a>. However, you
058: * can also specify hard keys and weak values, or any other
059: * combination. The default constructor uses hard keys
060: * and soft values, providing a memory-sensitive cache.<p>
061: *
062: * The algorithms used are basically the same as those
063: * in {@link java.util.HashMap}. In particular, you
064: * can specify a load factor and capacity to suit your
065: * needs. All optional {@link Map} operations are
066: * supported.<p>
067: *
068: * However, this {@link Map} implementation does <I>not</I>
069: * allow null elements. Attempting to add a null key or
070: * or a null value to the map will raise a
071: * <code>NullPointerException</code>.<p>
072: *
073: * As usual, this implementation is not synchronized. You
074: * can use {@link java.util.Collections#synchronizedMap} to
075: * provide synchronized access to a <code>ReferenceMap</code>.
076: *
077: * @see java.lang.ref.Reference
078: *
079: * @deprecated Moved to map subpackage. Due to be removed in v4.0.
080: * @since Commons Collections 2.1
081: * @version $Revision: 438363 $ $Date: 2006-08-30 05:48:00 +0100 (Wed, 30 Aug 2006) $
082: *
083: * @author Paul Jack
084: */
085: public class ReferenceMap extends AbstractMap {
086:
087: /**
088: * For serialization.
089: */
090: final private static long serialVersionUID = -3370601314380922368L;
091:
092: /**
093: * Constant indicating that hard references should be used.
094: */
095: final public static int HARD = 0;
096:
097: /**
098: * Constant indicating that soft references should be used.
099: */
100: final public static int SOFT = 1;
101:
102: /**
103: * Constant indicating that weak references should be used.
104: */
105: final public static int WEAK = 2;
106:
107: // --- serialized instance variables:
108:
109: /**
110: * The reference type for keys. Must be HARD, SOFT, WEAK.
111: * Note: I originally marked this field as final, but then this class
112: * didn't compile under JDK1.2.2.
113: * @serial
114: */
115: private int keyType;
116:
117: /**
118: * The reference type for values. Must be HARD, SOFT, WEAK.
119: * Note: I originally marked this field as final, but then this class
120: * didn't compile under JDK1.2.2.
121: * @serial
122: */
123: private int valueType;
124:
125: /**
126: * The threshold variable is calculated by multiplying
127: * table.length and loadFactor.
128: * Note: I originally marked this field as final, but then this class
129: * didn't compile under JDK1.2.2.
130: * @serial
131: */
132: private float loadFactor;
133:
134: /**
135: * Should the value be automatically purged when the associated key has been collected?
136: */
137: private boolean purgeValues = false;
138:
139: // -- Non-serialized instance variables
140:
141: /**
142: * ReferenceQueue used to eliminate stale mappings.
143: * See purge.
144: */
145: private transient ReferenceQueue queue = new ReferenceQueue();
146:
147: /**
148: * The hash table. Its length is always a power of two.
149: */
150: private transient Entry[] table;
151:
152: /**
153: * Number of mappings in this map.
154: */
155: private transient int size;
156:
157: /**
158: * When size reaches threshold, the map is resized.
159: * See resize().
160: */
161: private transient int threshold;
162:
163: /**
164: * Number of times this map has been modified.
165: */
166: private transient volatile int modCount;
167:
168: /**
169: * Cached key set. May be null if key set is never accessed.
170: */
171: private transient Set keySet;
172:
173: /**
174: * Cached entry set. May be null if entry set is never accessed.
175: */
176: private transient Set entrySet;
177:
178: /**
179: * Cached values. May be null if values() is never accessed.
180: */
181: private transient Collection values;
182:
183: /**
184: * Constructs a new <code>ReferenceMap</code> that will
185: * use hard references to keys and soft references to values.
186: */
187: public ReferenceMap() {
188: this (HARD, SOFT);
189: }
190:
191: /**
192: * Constructs a new <code>ReferenceMap</code> that will
193: * use the specified types of references.
194: *
195: * @param keyType the type of reference to use for keys;
196: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
197: * @param valueType the type of reference to use for values;
198: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
199: * @param purgeValues should the value be automatically purged when the
200: * key is garbage collected
201: */
202: public ReferenceMap(int keyType, int valueType, boolean purgeValues) {
203: this (keyType, valueType);
204: this .purgeValues = purgeValues;
205: }
206:
207: /**
208: * Constructs a new <code>ReferenceMap</code> that will
209: * use the specified types of references.
210: *
211: * @param keyType the type of reference to use for keys;
212: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
213: * @param valueType the type of reference to use for values;
214: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
215: */
216: public ReferenceMap(int keyType, int valueType) {
217: this (keyType, valueType, 16, 0.75f);
218: }
219:
220: /**
221: * Constructs a new <code>ReferenceMap</code> with the
222: * specified reference types, load factor and initial
223: * capacity.
224: *
225: * @param keyType the type of reference to use for keys;
226: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
227: * @param valueType the type of reference to use for values;
228: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
229: * @param capacity the initial capacity for the map
230: * @param loadFactor the load factor for the map
231: * @param purgeValues should the value be automatically purged when the
232: * key is garbage collected
233: */
234: public ReferenceMap(int keyType, int valueType, int capacity,
235: float loadFactor, boolean purgeValues) {
236: this (keyType, valueType, capacity, loadFactor);
237: this .purgeValues = purgeValues;
238: }
239:
240: /**
241: * Constructs a new <code>ReferenceMap</code> with the
242: * specified reference types, load factor and initial
243: * capacity.
244: *
245: * @param keyType the type of reference to use for keys;
246: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
247: * @param valueType the type of reference to use for values;
248: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
249: * @param capacity the initial capacity for the map
250: * @param loadFactor the load factor for the map
251: */
252: public ReferenceMap(int keyType, int valueType, int capacity,
253: float loadFactor) {
254: super ();
255:
256: verify("keyType", keyType);
257: verify("valueType", valueType);
258:
259: if (capacity <= 0) {
260: throw new IllegalArgumentException(
261: "capacity must be positive");
262: }
263: if ((loadFactor <= 0.0f) || (loadFactor >= 1.0f)) {
264: throw new IllegalArgumentException(
265: "Load factor must be greater than 0 and less than 1.");
266: }
267:
268: this .keyType = keyType;
269: this .valueType = valueType;
270:
271: int v = 1;
272: while (v < capacity)
273: v *= 2;
274:
275: this .table = new Entry[v];
276: this .loadFactor = loadFactor;
277: this .threshold = (int) (v * loadFactor);
278: }
279:
280: // used by constructor
281: private static void verify(String name, int type) {
282: if ((type < HARD) || (type > WEAK)) {
283: throw new IllegalArgumentException(name
284: + " must be HARD, SOFT, WEAK.");
285: }
286: }
287:
288: /**
289: * Writes this object to the given output stream.
290: *
291: * @param out the output stream to write to
292: * @throws IOException if the stream raises it
293: */
294: private void writeObject(ObjectOutputStream out) throws IOException {
295: out.defaultWriteObject();
296: out.writeInt(table.length);
297:
298: // Have to use null-terminated list because size might shrink
299: // during iteration
300:
301: for (Iterator iter = entrySet().iterator(); iter.hasNext();) {
302: Map.Entry entry = (Map.Entry) iter.next();
303: out.writeObject(entry.getKey());
304: out.writeObject(entry.getValue());
305: }
306: out.writeObject(null);
307: }
308:
309: /**
310: * Reads the contents of this object from the given input stream.
311: *
312: * @param inp the input stream to read from
313: * @throws IOException if the stream raises it
314: * @throws ClassNotFoundException if the stream raises it
315: */
316: private void readObject(ObjectInputStream inp) throws IOException,
317: ClassNotFoundException {
318: inp.defaultReadObject();
319: table = new Entry[inp.readInt()];
320: threshold = (int) (table.length * loadFactor);
321: queue = new ReferenceQueue();
322: Object key = inp.readObject();
323: while (key != null) {
324: Object value = inp.readObject();
325: put(key, value);
326: key = inp.readObject();
327: }
328: }
329:
330: /**
331: * Constructs a reference of the given type to the given
332: * referent. The reference is registered with the queue
333: * for later purging.
334: *
335: * @param type HARD, SOFT or WEAK
336: * @param referent the object to refer to
337: * @param hash the hash code of the <I>key</I> of the mapping;
338: * this number might be different from referent.hashCode() if
339: * the referent represents a value and not a key
340: */
341: private Object toReference(int type, Object referent, int hash) {
342: switch (type) {
343: case HARD:
344: return referent;
345: case SOFT:
346: return new SoftRef(hash, referent, queue);
347: case WEAK:
348: return new WeakRef(hash, referent, queue);
349: default:
350: throw new Error();
351: }
352: }
353:
354: /**
355: * Returns the entry associated with the given key.
356: *
357: * @param key the key of the entry to look up
358: * @return the entry associated with that key, or null
359: * if the key is not in this map
360: */
361: private Entry getEntry(Object key) {
362: if (key == null)
363: return null;
364: int hash = key.hashCode();
365: int index = indexFor(hash);
366: for (Entry entry = table[index]; entry != null; entry = entry.next) {
367: if ((entry.hash == hash) && key.equals(entry.getKey())) {
368: return entry;
369: }
370: }
371: return null;
372: }
373:
374: /**
375: * Converts the given hash code into an index into the
376: * hash table.
377: */
378: private int indexFor(int hash) {
379: // mix the bits to avoid bucket collisions...
380: hash += ~(hash << 15);
381: hash ^= (hash >>> 10);
382: hash += (hash << 3);
383: hash ^= (hash >>> 6);
384: hash += ~(hash << 11);
385: hash ^= (hash >>> 16);
386: return hash & (table.length - 1);
387: }
388:
389: /**
390: * Resizes this hash table by doubling its capacity.
391: * This is an expensive operation, as entries must
392: * be copied from the old smaller table to the new
393: * bigger table.
394: */
395: private void resize() {
396: Entry[] old = table;
397: table = new Entry[old.length * 2];
398:
399: for (int i = 0; i < old.length; i++) {
400: Entry next = old[i];
401: while (next != null) {
402: Entry entry = next;
403: next = next.next;
404: int index = indexFor(entry.hash);
405: entry.next = table[index];
406: table[index] = entry;
407: }
408: old[i] = null;
409: }
410: threshold = (int) (table.length * loadFactor);
411: }
412:
413: /**
414: * Purges stale mappings from this map.
415: * <p>
416: * Ordinarily, stale mappings are only removed during
417: * a write operation, although this method is called for both
418: * read and write operations to maintain a consistent state.
419: * <p>
420: * Note that this method is not synchronized! Special
421: * care must be taken if, for instance, you want stale
422: * mappings to be removed on a periodic basis by some
423: * background thread.
424: */
425: private void purge() {
426: Reference ref = queue.poll();
427: while (ref != null) {
428: purge(ref);
429: ref = queue.poll();
430: }
431: }
432:
433: private void purge(Reference ref) {
434: // The hashCode of the reference is the hashCode of the
435: // mapping key, even if the reference refers to the
436: // mapping value...
437: int hash = ref.hashCode();
438: int index = indexFor(hash);
439: Entry previous = null;
440: Entry entry = table[index];
441: while (entry != null) {
442: if (entry.purge(ref)) {
443: if (previous == null)
444: table[index] = entry.next;
445: else
446: previous.next = entry.next;
447: this .size--;
448: return;
449: }
450: previous = entry;
451: entry = entry.next;
452: }
453:
454: }
455:
456: /**
457: * Returns the size of this map.
458: *
459: * @return the size of this map
460: */
461: public int size() {
462: purge();
463: return size;
464: }
465:
466: /**
467: * Returns <code>true</code> if this map is empty.
468: *
469: * @return <code>true</code> if this map is empty
470: */
471: public boolean isEmpty() {
472: purge();
473: return size == 0;
474: }
475:
476: /**
477: * Returns <code>true</code> if this map contains the given key.
478: *
479: * @return true if the given key is in this map
480: */
481: public boolean containsKey(Object key) {
482: purge();
483: Entry entry = getEntry(key);
484: if (entry == null)
485: return false;
486: return entry.getValue() != null;
487: }
488:
489: /**
490: * Returns the value associated with the given key, if any.
491: *
492: * @return the value associated with the given key, or <code>null</code>
493: * if the key maps to no value
494: */
495: public Object get(Object key) {
496: purge();
497: Entry entry = getEntry(key);
498: if (entry == null)
499: return null;
500: return entry.getValue();
501: }
502:
503: /**
504: * Associates the given key with the given value.<p>
505: * Neither the key nor the value may be null.
506: *
507: * @param key the key of the mapping
508: * @param value the value of the mapping
509: * @return the last value associated with that key, or
510: * null if no value was associated with the key
511: * @throws NullPointerException if either the key or value
512: * is null
513: */
514: public Object put(Object key, Object value) {
515: if (key == null)
516: throw new NullPointerException("null keys not allowed");
517: if (value == null)
518: throw new NullPointerException("null values not allowed");
519:
520: purge();
521: if (size + 1 > threshold)
522: resize();
523:
524: int hash = key.hashCode();
525: int index = indexFor(hash);
526: Entry entry = table[index];
527: while (entry != null) {
528: if ((hash == entry.hash) && key.equals(entry.getKey())) {
529: Object result = entry.getValue();
530: entry.setValue(value);
531: return result;
532: }
533: entry = entry.next;
534: }
535: this .size++;
536: modCount++;
537: key = toReference(keyType, key, hash);
538: value = toReference(valueType, value, hash);
539: table[index] = new Entry(key, hash, value, table[index]);
540: return null;
541: }
542:
543: /**
544: * Removes the key and its associated value from this map.
545: *
546: * @param key the key to remove
547: * @return the value associated with that key, or null if
548: * the key was not in the map
549: */
550: public Object remove(Object key) {
551: if (key == null)
552: return null;
553: purge();
554: int hash = key.hashCode();
555: int index = indexFor(hash);
556: Entry previous = null;
557: Entry entry = table[index];
558: while (entry != null) {
559: if ((hash == entry.hash) && key.equals(entry.getKey())) {
560: if (previous == null)
561: table[index] = entry.next;
562: else
563: previous.next = entry.next;
564: this .size--;
565: modCount++;
566: return entry.getValue();
567: }
568: previous = entry;
569: entry = entry.next;
570: }
571: return null;
572: }
573:
574: /**
575: * Clears this map.
576: */
577: public void clear() {
578: Arrays.fill(table, null);
579: size = 0;
580: while (queue.poll() != null)
581: ; // drain the queue
582: }
583:
584: /**
585: * Returns a set view of this map's entries.
586: *
587: * @return a set view of this map's entries
588: */
589: public Set entrySet() {
590: if (entrySet != null) {
591: return entrySet;
592: }
593: entrySet = new AbstractSet() {
594: public int size() {
595: return ReferenceMap.this .size();
596: }
597:
598: public void clear() {
599: ReferenceMap.this .clear();
600: }
601:
602: public boolean contains(Object o) {
603: if (o == null)
604: return false;
605: if (!(o instanceof Map.Entry))
606: return false;
607: Map.Entry e = (Map.Entry) o;
608: Entry e2 = getEntry(e.getKey());
609: return (e2 != null) && e.equals(e2);
610: }
611:
612: public boolean remove(Object o) {
613: boolean r = contains(o);
614: if (r) {
615: Map.Entry e = (Map.Entry) o;
616: ReferenceMap.this .remove(e.getKey());
617: }
618: return r;
619: }
620:
621: public Iterator iterator() {
622: return new EntryIterator();
623: }
624:
625: public Object[] toArray() {
626: return toArray(new Object[0]);
627: }
628:
629: public Object[] toArray(Object[] arr) {
630: ArrayList list = new ArrayList();
631: Iterator iterator = iterator();
632: while (iterator.hasNext()) {
633: Entry e = (Entry) iterator.next();
634: list.add(new DefaultMapEntry(e.getKey(), e
635: .getValue()));
636: }
637: return list.toArray(arr);
638: }
639: };
640: return entrySet;
641: }
642:
643: /**
644: * Returns a set view of this map's keys.
645: *
646: * @return a set view of this map's keys
647: */
648: public Set keySet() {
649: if (keySet != null)
650: return keySet;
651: keySet = new AbstractSet() {
652: public int size() {
653: return ReferenceMap.this .size();
654: }
655:
656: public Iterator iterator() {
657: return new KeyIterator();
658: }
659:
660: public boolean contains(Object o) {
661: return containsKey(o);
662: }
663:
664: public boolean remove(Object o) {
665: Object r = ReferenceMap.this .remove(o);
666: return r != null;
667: }
668:
669: public void clear() {
670: ReferenceMap.this .clear();
671: }
672:
673: public Object[] toArray() {
674: return toArray(new Object[0]);
675: }
676:
677: public Object[] toArray(Object[] array) {
678: Collection c = new ArrayList(size());
679: for (Iterator it = iterator(); it.hasNext();) {
680: c.add(it.next());
681: }
682: return c.toArray(array);
683: }
684: };
685: return keySet;
686: }
687:
688: /**
689: * Returns a collection view of this map's values.
690: *
691: * @return a collection view of this map's values.
692: */
693: public Collection values() {
694: if (values != null)
695: return values;
696: values = new AbstractCollection() {
697: public int size() {
698: return ReferenceMap.this .size();
699: }
700:
701: public void clear() {
702: ReferenceMap.this .clear();
703: }
704:
705: public Iterator iterator() {
706: return new ValueIterator();
707: }
708:
709: public Object[] toArray() {
710: return toArray(new Object[0]);
711: }
712:
713: public Object[] toArray(Object[] array) {
714: Collection c = new ArrayList(size());
715: for (Iterator it = iterator(); it.hasNext();) {
716: c.add(it.next());
717: }
718: return c.toArray(array);
719: }
720: };
721: return values;
722: }
723:
724: // If getKey() or getValue() returns null, it means
725: // the mapping is stale and should be removed.
726: private class Entry implements Map.Entry, KeyValue {
727:
728: Object key;
729: Object value;
730: int hash;
731: Entry next;
732:
733: public Entry(Object key, int hash, Object value, Entry next) {
734: this .key = key;
735: this .hash = hash;
736: this .value = value;
737: this .next = next;
738: }
739:
740: public Object getKey() {
741: return (keyType > HARD) ? ((Reference) key).get() : key;
742: }
743:
744: public Object getValue() {
745: return (valueType > HARD) ? ((Reference) value).get()
746: : value;
747: }
748:
749: public Object setValue(Object object) {
750: Object old = getValue();
751: if (valueType > HARD)
752: ((Reference) value).clear();
753: value = toReference(valueType, object, hash);
754: return old;
755: }
756:
757: public boolean equals(Object o) {
758: if (o == null)
759: return false;
760: if (o == this )
761: return true;
762: if (!(o instanceof Map.Entry))
763: return false;
764:
765: Map.Entry entry = (Map.Entry) o;
766: Object key = entry.getKey();
767: Object value = entry.getValue();
768: if ((key == null) || (value == null))
769: return false;
770: return key.equals(getKey()) && value.equals(getValue());
771: }
772:
773: public int hashCode() {
774: Object v = getValue();
775: return hash ^ ((v == null) ? 0 : v.hashCode());
776: }
777:
778: public String toString() {
779: return getKey() + "=" + getValue();
780: }
781:
782: boolean purge(Reference ref) {
783: boolean r = (keyType > HARD) && (key == ref);
784: r = r || ((valueType > HARD) && (value == ref));
785: if (r) {
786: if (keyType > HARD)
787: ((Reference) key).clear();
788: if (valueType > HARD) {
789: ((Reference) value).clear();
790: } else if (purgeValues) {
791: value = null;
792: }
793: }
794: return r;
795: }
796: }
797:
798: private class EntryIterator implements Iterator {
799: // These fields keep track of where we are in the table.
800: int index;
801: Entry entry;
802: Entry previous;
803:
804: // These Object fields provide hard references to the
805: // current and next entry; this assures that if hasNext()
806: // returns true, next() will actually return a valid element.
807: Object nextKey, nextValue;
808: Object currentKey, currentValue;
809:
810: int expectedModCount;
811:
812: public EntryIterator() {
813: index = (size() != 0 ? table.length : 0);
814: // have to do this here! size() invocation above
815: // may have altered the modCount.
816: expectedModCount = modCount;
817: }
818:
819: public boolean hasNext() {
820: checkMod();
821: while (nextNull()) {
822: Entry e = entry;
823: int i = index;
824: while ((e == null) && (i > 0)) {
825: i--;
826: e = table[i];
827: }
828: entry = e;
829: index = i;
830: if (e == null) {
831: currentKey = null;
832: currentValue = null;
833: return false;
834: }
835: nextKey = e.getKey();
836: nextValue = e.getValue();
837: if (nextNull())
838: entry = entry.next;
839: }
840: return true;
841: }
842:
843: private void checkMod() {
844: if (modCount != expectedModCount) {
845: throw new ConcurrentModificationException();
846: }
847: }
848:
849: private boolean nextNull() {
850: return (nextKey == null) || (nextValue == null);
851: }
852:
853: protected Entry nextEntry() {
854: checkMod();
855: if (nextNull() && !hasNext())
856: throw new NoSuchElementException();
857: previous = entry;
858: entry = entry.next;
859: currentKey = nextKey;
860: currentValue = nextValue;
861: nextKey = null;
862: nextValue = null;
863: return previous;
864: }
865:
866: public Object next() {
867: return nextEntry();
868: }
869:
870: public void remove() {
871: checkMod();
872: if (previous == null)
873: throw new IllegalStateException();
874: ReferenceMap.this .remove(currentKey);
875: previous = null;
876: currentKey = null;
877: currentValue = null;
878: expectedModCount = modCount;
879: }
880:
881: }
882:
883: private class ValueIterator extends EntryIterator {
884: public Object next() {
885: return nextEntry().getValue();
886: }
887: }
888:
889: private class KeyIterator extends EntryIterator {
890: public Object next() {
891: return nextEntry().getKey();
892: }
893: }
894:
895: // These two classes store the hashCode of the key of
896: // of the mapping, so that after they're dequeued a quick
897: // lookup of the bucket in the table can occur.
898:
899: private static class SoftRef extends SoftReference {
900: private int hash;
901:
902: public SoftRef(int hash, Object r, ReferenceQueue q) {
903: super (r, q);
904: this .hash = hash;
905: }
906:
907: public int hashCode() {
908: return hash;
909: }
910: }
911:
912: private static class WeakRef extends WeakReference {
913: private int hash;
914:
915: public WeakRef(int hash, Object r, ReferenceQueue q) {
916: super (r, q);
917: this .hash = hash;
918: }
919:
920: public int hashCode() {
921: return hash;
922: }
923: }
924:
925: }
|