1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections;
  17. import java.io.IOException;
  18. import java.io.ObjectInputStream;
  19. import java.io.ObjectOutputStream;
  20. import java.lang.ref.Reference;
  21. import java.lang.ref.ReferenceQueue;
  22. import java.lang.ref.SoftReference;
  23. import java.lang.ref.WeakReference;
  24. import java.util.AbstractCollection;
  25. import java.util.AbstractMap;
  26. import java.util.AbstractSet;
  27. import java.util.ArrayList;
  28. import java.util.Arrays;
  29. import java.util.Collection;
  30. import java.util.ConcurrentModificationException;
  31. import java.util.Iterator;
  32. import java.util.Map;
  33. import java.util.NoSuchElementException;
  34. import java.util.Set;
  35. import org.apache.commons.collections.keyvalue.DefaultMapEntry;
  36. /**
  37. * Hash-based {@link Map} implementation that allows
  38. * mappings to be removed by the garbage collector.<p>
  39. *
  40. * When you construct a <code>ReferenceMap</code>, you can
  41. * specify what kind of references are used to store the
  42. * map's keys and values. If non-hard references are
  43. * used, then the garbage collector can remove mappings
  44. * if a key or value becomes unreachable, or if the
  45. * JVM's memory is running low. For information on how
  46. * the different reference types behave, see
  47. * {@link Reference}.<p>
  48. *
  49. * Different types of references can be specified for keys
  50. * and values. The keys can be configured to be weak but
  51. * the values hard, in which case this class will behave
  52. * like a <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
  53. * <code>WeakHashMap</code></a>. However, you
  54. * can also specify hard keys and weak values, or any other
  55. * combination. The default constructor uses hard keys
  56. * and soft values, providing a memory-sensitive cache.<p>
  57. *
  58. * The algorithms used are basically the same as those
  59. * in {@link java.util.HashMap}. In particular, you
  60. * can specify a load factor and capacity to suit your
  61. * needs. All optional {@link Map} operations are
  62. * supported.<p>
  63. *
  64. * However, this {@link Map} implementation does <I>not</I>
  65. * allow null elements. Attempting to add a null key or
  66. * or a null value to the map will raise a
  67. * <code>NullPointerException</code>.<p>
  68. *
  69. * As usual, this implementation is not synchronized. You
  70. * can use {@link java.util.Collections#synchronizedMap} to
  71. * provide synchronized access to a <code>ReferenceMap</code>.
  72. *
  73. * @see java.lang.ref.Reference
  74. *
  75. * @deprecated Moved to map subpackage. Due to be removed in v4.0.
  76. * @since Commons Collections 2.1
  77. * @version $Revision: 1.22 $ $Date: 2004/02/18 01:15:42 $
  78. *
  79. * @author Paul Jack
  80. */
  81. public class ReferenceMap extends AbstractMap {
  82. /**
  83. * For serialization.
  84. */
  85. final private static long serialVersionUID = -3370601314380922368L;
  86. /**
  87. * Constant indicating that hard references should be used.
  88. */
  89. final public static int HARD = 0;
  90. /**
  91. * Constant indicating that soft references should be used.
  92. */
  93. final public static int SOFT = 1;
  94. /**
  95. * Constant indicating that weak references should be used.
  96. */
  97. final public static int WEAK = 2;
  98. // --- serialized instance variables:
  99. /**
  100. * The reference type for keys. Must be HARD, SOFT, WEAK.
  101. * Note: I originally marked this field as final, but then this class
  102. * didn't compile under JDK1.2.2.
  103. * @serial
  104. */
  105. private int keyType;
  106. /**
  107. * The reference type for values. Must be HARD, SOFT, WEAK.
  108. * Note: I originally marked this field as final, but then this class
  109. * didn't compile under JDK1.2.2.
  110. * @serial
  111. */
  112. private int valueType;
  113. /**
  114. * The threshold variable is calculated by multiplying
  115. * table.length and loadFactor.
  116. * Note: I originally marked this field as final, but then this class
  117. * didn't compile under JDK1.2.2.
  118. * @serial
  119. */
  120. private float loadFactor;
  121. /**
  122. * Should the value be automatically purged when the associated key has been collected?
  123. */
  124. private boolean purgeValues = false;
  125. // -- Non-serialized instance variables
  126. /**
  127. * ReferenceQueue used to eliminate stale mappings.
  128. * See purge.
  129. */
  130. private transient ReferenceQueue queue = new ReferenceQueue();
  131. /**
  132. * The hash table. Its length is always a power of two.
  133. */
  134. private transient Entry[] table;
  135. /**
  136. * Number of mappings in this map.
  137. */
  138. private transient int size;
  139. /**
  140. * When size reaches threshold, the map is resized.
  141. * See resize().
  142. */
  143. private transient int threshold;
  144. /**
  145. * Number of times this map has been modified.
  146. */
  147. private transient volatile int modCount;
  148. /**
  149. * Cached key set. May be null if key set is never accessed.
  150. */
  151. private transient Set keySet;
  152. /**
  153. * Cached entry set. May be null if entry set is never accessed.
  154. */
  155. private transient Set entrySet;
  156. /**
  157. * Cached values. May be null if values() is never accessed.
  158. */
  159. private transient Collection values;
  160. /**
  161. * Constructs a new <code>ReferenceMap</code> that will
  162. * use hard references to keys and soft references to values.
  163. */
  164. public ReferenceMap() {
  165. this(HARD, SOFT);
  166. }
  167. /**
  168. * Constructs a new <code>ReferenceMap</code> that will
  169. * use the specified types of references.
  170. *
  171. * @param keyType the type of reference to use for keys;
  172. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  173. * @param valueType the type of reference to use for values;
  174. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  175. * @param purgeValues should the value be automatically purged when the
  176. * key is garbage collected
  177. */
  178. public ReferenceMap(int keyType, int valueType, boolean purgeValues) {
  179. this(keyType, valueType);
  180. this.purgeValues = purgeValues;
  181. }
  182. /**
  183. * Constructs a new <code>ReferenceMap</code> that will
  184. * use the specified types of references.
  185. *
  186. * @param keyType the type of reference to use for keys;
  187. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  188. * @param valueType the type of reference to use for values;
  189. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  190. */
  191. public ReferenceMap(int keyType, int valueType) {
  192. this(keyType, valueType, 16, 0.75f);
  193. }
  194. /**
  195. * Constructs a new <code>ReferenceMap</code> with the
  196. * specified reference types, load factor and initial
  197. * capacity.
  198. *
  199. * @param keyType the type of reference to use for keys;
  200. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  201. * @param valueType the type of reference to use for values;
  202. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  203. * @param capacity the initial capacity for the map
  204. * @param loadFactor the load factor for the map
  205. * @param purgeValues should the value be automatically purged when the
  206. * key is garbage collected
  207. */
  208. public ReferenceMap(
  209. int keyType,
  210. int valueType,
  211. int capacity,
  212. float loadFactor,
  213. boolean purgeValues) {
  214. this(keyType, valueType, capacity, loadFactor);
  215. this.purgeValues = purgeValues;
  216. }
  217. /**
  218. * Constructs a new <code>ReferenceMap</code> with the
  219. * specified reference types, load factor and initial
  220. * capacity.
  221. *
  222. * @param keyType the type of reference to use for keys;
  223. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  224. * @param valueType the type of reference to use for values;
  225. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  226. * @param capacity the initial capacity for the map
  227. * @param loadFactor the load factor for the map
  228. */
  229. public ReferenceMap(int keyType, int valueType, int capacity, float loadFactor) {
  230. super();
  231. verify("keyType", keyType);
  232. verify("valueType", valueType);
  233. if (capacity <= 0) {
  234. throw new IllegalArgumentException("capacity must be positive");
  235. }
  236. if ((loadFactor <= 0.0f) || (loadFactor >= 1.0f)) {
  237. throw new IllegalArgumentException("Load factor must be greater than 0 and less than 1.");
  238. }
  239. this.keyType = keyType;
  240. this.valueType = valueType;
  241. int v = 1;
  242. while (v < capacity) v *= 2;
  243. this.table = new Entry[v];
  244. this.loadFactor = loadFactor;
  245. this.threshold = (int)(v * loadFactor);
  246. }
  247. // used by constructor
  248. private static void verify(String name, int type) {
  249. if ((type < HARD) || (type > WEAK)) {
  250. throw new IllegalArgumentException(name +
  251. " must be HARD, SOFT, WEAK.");
  252. }
  253. }
  254. /**
  255. * Writes this object to the given output stream.
  256. *
  257. * @param out the output stream to write to
  258. * @throws IOException if the stream raises it
  259. */
  260. private void writeObject(ObjectOutputStream out) throws IOException {
  261. out.defaultWriteObject();
  262. out.writeInt(table.length);
  263. // Have to use null-terminated list because size might shrink
  264. // during iteration
  265. for (Iterator iter = entrySet().iterator(); iter.hasNext();) {
  266. Map.Entry entry = (Map.Entry)iter.next();
  267. out.writeObject(entry.getKey());
  268. out.writeObject(entry.getValue());
  269. }
  270. out.writeObject(null);
  271. }
  272. /**
  273. * Reads the contents of this object from the given input stream.
  274. *
  275. * @param inp the input stream to read from
  276. * @throws IOException if the stream raises it
  277. * @throws ClassNotFoundException if the stream raises it
  278. */
  279. private void readObject(ObjectInputStream inp) throws IOException, ClassNotFoundException {
  280. inp.defaultReadObject();
  281. table = new Entry[inp.readInt()];
  282. threshold = (int)(table.length * loadFactor);
  283. queue = new ReferenceQueue();
  284. Object key = inp.readObject();
  285. while (key != null) {
  286. Object value = inp.readObject();
  287. put(key, value);
  288. key = inp.readObject();
  289. }
  290. }
  291. /**
  292. * Constructs a reference of the given type to the given
  293. * referent. The reference is registered with the queue
  294. * for later purging.
  295. *
  296. * @param type HARD, SOFT or WEAK
  297. * @param referent the object to refer to
  298. * @param hash the hash code of the <I>key</I> of the mapping;
  299. * this number might be different from referent.hashCode() if
  300. * the referent represents a value and not a key
  301. */
  302. private Object toReference(int type, Object referent, int hash) {
  303. switch (type) {
  304. case HARD: return referent;
  305. case SOFT: return new SoftRef(hash, referent, queue);
  306. case WEAK: return new WeakRef(hash, referent, queue);
  307. default: throw new Error();
  308. }
  309. }
  310. /**
  311. * Returns the entry associated with the given key.
  312. *
  313. * @param key the key of the entry to look up
  314. * @return the entry associated with that key, or null
  315. * if the key is not in this map
  316. */
  317. private Entry getEntry(Object key) {
  318. if (key == null) return null;
  319. int hash = key.hashCode();
  320. int index = indexFor(hash);
  321. for (Entry entry = table[index]; entry != null; entry = entry.next) {
  322. if ((entry.hash == hash) && key.equals(entry.getKey())) {
  323. return entry;
  324. }
  325. }
  326. return null;
  327. }
  328. /**
  329. * Converts the given hash code into an index into the
  330. * hash table.
  331. */
  332. private int indexFor(int hash) {
  333. // mix the bits to avoid bucket collisions...
  334. hash += ~(hash << 15);
  335. hash ^= (hash >>> 10);
  336. hash += (hash << 3);
  337. hash ^= (hash >>> 6);
  338. hash += ~(hash << 11);
  339. hash ^= (hash >>> 16);
  340. return hash & (table.length - 1);
  341. }
  342. /**
  343. * Resizes this hash table by doubling its capacity.
  344. * This is an expensive operation, as entries must
  345. * be copied from the old smaller table to the new
  346. * bigger table.
  347. */
  348. private void resize() {
  349. Entry[] old = table;
  350. table = new Entry[old.length * 2];
  351. for (int i = 0; i < old.length; i++) {
  352. Entry next = old[i];
  353. while (next != null) {
  354. Entry entry = next;
  355. next = next.next;
  356. int index = indexFor(entry.hash);
  357. entry.next = table[index];
  358. table[index] = entry;
  359. }
  360. old[i] = null;
  361. }
  362. threshold = (int)(table.length * loadFactor);
  363. }
  364. /**
  365. * Purges stale mappings from this map.
  366. * <p>
  367. * Ordinarily, stale mappings are only removed during
  368. * a write operation, although this method is called for both
  369. * read and write operations to maintain a consistent state.
  370. * <p>
  371. * Note that this method is not synchronized! Special
  372. * care must be taken if, for instance, you want stale
  373. * mappings to be removed on a periodic basis by some
  374. * background thread.
  375. */
  376. private void purge() {
  377. Reference ref = queue.poll();
  378. while (ref != null) {
  379. purge(ref);
  380. ref = queue.poll();
  381. }
  382. }
  383. private void purge(Reference ref) {
  384. // The hashCode of the reference is the hashCode of the
  385. // mapping key, even if the reference refers to the
  386. // mapping value...
  387. int hash = ref.hashCode();
  388. int index = indexFor(hash);
  389. Entry previous = null;
  390. Entry entry = table[index];
  391. while (entry != null) {
  392. if (entry.purge(ref)) {
  393. if (previous == null) table[index] = entry.next;
  394. else previous.next = entry.next;
  395. this.size--;
  396. return;
  397. }
  398. previous = entry;
  399. entry = entry.next;
  400. }
  401. }
  402. /**
  403. * Returns the size of this map.
  404. *
  405. * @return the size of this map
  406. */
  407. public int size() {
  408. purge();
  409. return size;
  410. }
  411. /**
  412. * Returns <code>true</code> if this map is empty.
  413. *
  414. * @return <code>true</code> if this map is empty
  415. */
  416. public boolean isEmpty() {
  417. purge();
  418. return size == 0;
  419. }
  420. /**
  421. * Returns <code>true</code> if this map contains the given key.
  422. *
  423. * @return true if the given key is in this map
  424. */
  425. public boolean containsKey(Object key) {
  426. purge();
  427. Entry entry = getEntry(key);
  428. if (entry == null) return false;
  429. return entry.getValue() != null;
  430. }
  431. /**
  432. * Returns the value associated with the given key, if any.
  433. *
  434. * @return the value associated with the given key, or <code>null</code>
  435. * if the key maps to no value
  436. */
  437. public Object get(Object key) {
  438. purge();
  439. Entry entry = getEntry(key);
  440. if (entry == null) return null;
  441. return entry.getValue();
  442. }
  443. /**
  444. * Associates the given key with the given value.<p>
  445. * Neither the key nor the value may be null.
  446. *
  447. * @param key the key of the mapping
  448. * @param value the value of the mapping
  449. * @return the last value associated with that key, or
  450. * null if no value was associated with the key
  451. * @throws NullPointerException if either the key or value
  452. * is null
  453. */
  454. public Object put(Object key, Object value) {
  455. if (key == null) throw new NullPointerException("null keys not allowed");
  456. if (value == null) throw new NullPointerException("null values not allowed");
  457. purge();
  458. if (size + 1 > threshold) resize();
  459. int hash = key.hashCode();
  460. int index = indexFor(hash);
  461. Entry entry = table[index];
  462. while (entry != null) {
  463. if ((hash == entry.hash) && key.equals(entry.getKey())) {
  464. Object result = entry.getValue();
  465. entry.setValue(value);
  466. return result;
  467. }
  468. entry = entry.next;
  469. }
  470. this.size++;
  471. modCount++;
  472. key = toReference(keyType, key, hash);
  473. value = toReference(valueType, value, hash);
  474. table[index] = new Entry(key, hash, value, table[index]);
  475. return null;
  476. }
  477. /**
  478. * Removes the key and its associated value from this map.
  479. *
  480. * @param key the key to remove
  481. * @return the value associated with that key, or null if
  482. * the key was not in the map
  483. */
  484. public Object remove(Object key) {
  485. if (key == null) return null;
  486. purge();
  487. int hash = key.hashCode();
  488. int index = indexFor(hash);
  489. Entry previous = null;
  490. Entry entry = table[index];
  491. while (entry != null) {
  492. if ((hash == entry.hash) && key.equals(entry.getKey())) {
  493. if (previous == null) table[index] = entry.next;
  494. else previous.next = entry.next;
  495. this.size--;
  496. modCount++;
  497. return entry.getValue();
  498. }
  499. previous = entry;
  500. entry = entry.next;
  501. }
  502. return null;
  503. }
  504. /**
  505. * Clears this map.
  506. */
  507. public void clear() {
  508. Arrays.fill(table, null);
  509. size = 0;
  510. while (queue.poll() != null); // drain the queue
  511. }
  512. /**
  513. * Returns a set view of this map's entries.
  514. *
  515. * @return a set view of this map's entries
  516. */
  517. public Set entrySet() {
  518. if (entrySet != null) {
  519. return entrySet;
  520. }
  521. entrySet = new AbstractSet() {
  522. public int size() {
  523. return ReferenceMap.this.size();
  524. }
  525. public void clear() {
  526. ReferenceMap.this.clear();
  527. }
  528. public boolean contains(Object o) {
  529. if (o == null) return false;
  530. if (!(o instanceof Map.Entry)) return false;
  531. Map.Entry e = (Map.Entry)o;
  532. Entry e2 = getEntry(e.getKey());
  533. return (e2 != null) && e.equals(e2);
  534. }
  535. public boolean remove(Object o) {
  536. boolean r = contains(o);
  537. if (r) {
  538. Map.Entry e = (Map.Entry)o;
  539. ReferenceMap.this.remove(e.getKey());
  540. }
  541. return r;
  542. }
  543. public Iterator iterator() {
  544. return new EntryIterator();
  545. }
  546. public Object[] toArray() {
  547. return toArray(new Object[0]);
  548. }
  549. public Object[] toArray(Object[] arr) {
  550. ArrayList list = new ArrayList();
  551. Iterator iterator = iterator();
  552. while (iterator.hasNext()) {
  553. Entry e = (Entry)iterator.next();
  554. list.add(new DefaultMapEntry(e.getKey(), e.getValue()));
  555. }
  556. return list.toArray(arr);
  557. }
  558. };
  559. return entrySet;
  560. }
  561. /**
  562. * Returns a set view of this map's keys.
  563. *
  564. * @return a set view of this map's keys
  565. */
  566. public Set keySet() {
  567. if (keySet != null) return keySet;
  568. keySet = new AbstractSet() {
  569. public int size() {
  570. return ReferenceMap.this.size();
  571. }
  572. public Iterator iterator() {
  573. return new KeyIterator();
  574. }
  575. public boolean contains(Object o) {
  576. return containsKey(o);
  577. }
  578. public boolean remove(Object o) {
  579. Object r = ReferenceMap.this.remove(o);
  580. return r != null;
  581. }
  582. public void clear() {
  583. ReferenceMap.this.clear();
  584. }
  585. public Object[] toArray() {
  586. return toArray(new Object[0]);
  587. }
  588. public Object[] toArray(Object[] array) {
  589. Collection c = new ArrayList(size());
  590. for (Iterator it = iterator(); it.hasNext(); ) {
  591. c.add(it.next());
  592. }
  593. return c.toArray(array);
  594. }
  595. };
  596. return keySet;
  597. }
  598. /**
  599. * Returns a collection view of this map's values.
  600. *
  601. * @return a collection view of this map's values.
  602. */
  603. public Collection values() {
  604. if (values != null) return values;
  605. values = new AbstractCollection() {
  606. public int size() {
  607. return ReferenceMap.this.size();
  608. }
  609. public void clear() {
  610. ReferenceMap.this.clear();
  611. }
  612. public Iterator iterator() {
  613. return new ValueIterator();
  614. }
  615. public Object[] toArray() {
  616. return toArray(new Object[0]);
  617. }
  618. public Object[] toArray(Object[] array) {
  619. Collection c = new ArrayList(size());
  620. for (Iterator it = iterator(); it.hasNext(); ) {
  621. c.add(it.next());
  622. }
  623. return c.toArray(array);
  624. }
  625. };
  626. return values;
  627. }
  628. // If getKey() or getValue() returns null, it means
  629. // the mapping is stale and should be removed.
  630. private class Entry implements Map.Entry, KeyValue {
  631. Object key;
  632. Object value;
  633. int hash;
  634. Entry next;
  635. public Entry(Object key, int hash, Object value, Entry next) {
  636. this.key = key;
  637. this.hash = hash;
  638. this.value = value;
  639. this.next = next;
  640. }
  641. public Object getKey() {
  642. return (keyType > HARD) ? ((Reference)key).get() : key;
  643. }
  644. public Object getValue() {
  645. return (valueType > HARD) ? ((Reference)value).get() : value;
  646. }
  647. public Object setValue(Object object) {
  648. Object old = getValue();
  649. if (valueType > HARD) ((Reference)value).clear();
  650. value = toReference(valueType, object, hash);
  651. return old;
  652. }
  653. public boolean equals(Object o) {
  654. if (o == null) return false;
  655. if (o == this) return true;
  656. if (!(o instanceof Map.Entry)) return false;
  657. Map.Entry entry = (Map.Entry)o;
  658. Object key = entry.getKey();
  659. Object value = entry.getValue();
  660. if ((key == null) || (value == null)) return false;
  661. return key.equals(getKey()) && value.equals(getValue());
  662. }
  663. public int hashCode() {
  664. Object v = getValue();
  665. return hash ^ ((v == null) ? 0 : v.hashCode());
  666. }
  667. public String toString() {
  668. return getKey() + "=" + getValue();
  669. }
  670. boolean purge(Reference ref) {
  671. boolean r = (keyType > HARD) && (key == ref);
  672. r = r || ((valueType > HARD) && (value == ref));
  673. if (r) {
  674. if (keyType > HARD) ((Reference)key).clear();
  675. if (valueType > HARD) {
  676. ((Reference)value).clear();
  677. } else if (purgeValues) {
  678. value = null;
  679. }
  680. }
  681. return r;
  682. }
  683. }
  684. private class EntryIterator implements Iterator {
  685. // These fields keep track of where we are in the table.
  686. int index;
  687. Entry entry;
  688. Entry previous;
  689. // These Object fields provide hard references to the
  690. // current and next entry; this assures that if hasNext()
  691. // returns true, next() will actually return a valid element.
  692. Object nextKey, nextValue;
  693. Object currentKey, currentValue;
  694. int expectedModCount;
  695. public EntryIterator() {
  696. index = (size() != 0 ? table.length : 0);
  697. // have to do this here! size() invocation above
  698. // may have altered the modCount.
  699. expectedModCount = modCount;
  700. }
  701. public boolean hasNext() {
  702. checkMod();
  703. while (nextNull()) {
  704. Entry e = entry;
  705. int i = index;
  706. while ((e == null) && (i > 0)) {
  707. i--;
  708. e = table[i];
  709. }
  710. entry = e;
  711. index = i;
  712. if (e == null) {
  713. currentKey = null;
  714. currentValue = null;
  715. return false;
  716. }
  717. nextKey = e.getKey();
  718. nextValue = e.getValue();
  719. if (nextNull()) entry = entry.next;
  720. }
  721. return true;
  722. }
  723. private void checkMod() {
  724. if (modCount != expectedModCount) {
  725. throw new ConcurrentModificationException();
  726. }
  727. }
  728. private boolean nextNull() {
  729. return (nextKey == null) || (nextValue == null);
  730. }
  731. protected Entry nextEntry() {
  732. checkMod();
  733. if (nextNull() && !hasNext()) throw new NoSuchElementException();
  734. previous = entry;
  735. entry = entry.next;
  736. currentKey = nextKey;
  737. currentValue = nextValue;
  738. nextKey = null;
  739. nextValue = null;
  740. return previous;
  741. }
  742. public Object next() {
  743. return nextEntry();
  744. }
  745. public void remove() {
  746. checkMod();
  747. if (previous == null) throw new IllegalStateException();
  748. ReferenceMap.this.remove(currentKey);
  749. previous = null;
  750. currentKey = null;
  751. currentValue = null;
  752. expectedModCount = modCount;
  753. }
  754. }
  755. private class ValueIterator extends EntryIterator {
  756. public Object next() {
  757. return nextEntry().getValue();
  758. }
  759. }
  760. private class KeyIterator extends EntryIterator {
  761. public Object next() {
  762. return nextEntry().getKey();
  763. }
  764. }
  765. // These two classes store the hashCode of the key of
  766. // of the mapping, so that after they're dequeued a quick
  767. // lookup of the bucket in the table can occur.
  768. private static class SoftRef extends SoftReference {
  769. private int hash;
  770. public SoftRef(int hash, Object r, ReferenceQueue q) {
  771. super(r, q);
  772. this.hash = hash;
  773. }
  774. public int hashCode() {
  775. return hash;
  776. }
  777. }
  778. private static class WeakRef extends WeakReference {
  779. private int hash;
  780. public WeakRef(int hash, Object r, ReferenceQueue q) {
  781. super(r, q);
  782. this.hash = hash;
  783. }
  784. public int hashCode() {
  785. return hash;
  786. }
  787. }
  788. }