1. /*
  2. * Copyright 2002-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.map;
  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.ArrayList;
  25. import java.util.Collection;
  26. import java.util.ConcurrentModificationException;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.NoSuchElementException;
  31. import java.util.Set;
  32. import org.apache.commons.collections.MapIterator;
  33. import org.apache.commons.collections.keyvalue.DefaultMapEntry;
  34. /**
  35. * An abstract implementation of a hash-based map that allows the entries to
  36. * be removed by the garbage collector.
  37. * <p>
  38. * This class implements all the features necessary for a subclass reference
  39. * hash-based map. Key-value entries are stored in instances of the
  40. * <code>ReferenceEntry</code> class which can be overridden and replaced.
  41. * The iterators can similarly be replaced, without the need to replace the KeySet,
  42. * EntrySet and Values view classes.
  43. * <p>
  44. * Overridable methods are provided to change the default hashing behaviour, and
  45. * to change how entries are added to and removed from the map. Hopefully, all you
  46. * need for unusual subclasses is here.
  47. * <p>
  48. * When you construct an <code>AbstractReferenceMap</code>, you can specify what
  49. * kind of references are used to store the map's keys and values.
  50. * If non-hard references are used, then the garbage collector can remove
  51. * mappings if a key or value becomes unreachable, or if the JVM's memory is
  52. * running low. For information on how the different reference types behave,
  53. * see {@link Reference}.
  54. * <p>
  55. * Different types of references can be specified for keys and values.
  56. * The keys can be configured to be weak but the values hard,
  57. * in which case this class will behave like a
  58. * <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
  59. * <code>WeakHashMap</code></a>. However, you can also specify hard keys and
  60. * weak values, or any other combination. The default constructor uses
  61. * hard keys and soft values, providing a memory-sensitive cache.
  62. * <p>
  63. * This {@link Map} implementation does <i>not</i> allow null elements.
  64. * Attempting to add a null key or value to the map will raise a
  65. * <code>NullPointerException</code>.
  66. * <p>
  67. * All the available iterators can be reset back to the start by casting to
  68. * <code>ResettableIterator</code> and calling <code>reset()</code>.
  69. * <p>
  70. * This implementation is not synchronized.
  71. * You can use {@link java.util.Collections#synchronizedMap} to
  72. * provide synchronized access to a <code>ReferenceMap</code>.
  73. *
  74. * @see java.lang.ref.Reference
  75. * @since Commons Collections 3.1 (extracted from ReferenceMap in 3.0)
  76. * @version $Revision: 1.3 $ $Date: 2004/06/07 22:14:42 $
  77. *
  78. * @author Paul Jack
  79. * @author Stephen Colebourne
  80. */
  81. public abstract class AbstractReferenceMap extends AbstractHashedMap {
  82. /** Constant indicating that hard references should be used */
  83. public static final int HARD = 0;
  84. /** Constant indicating that soft references should be used */
  85. public static final int SOFT = 1;
  86. /** Constant indicating that weak references should be used */
  87. public static final int WEAK = 2;
  88. /**
  89. * The reference type for keys. Must be HARD, SOFT, WEAK.
  90. * @serial
  91. */
  92. protected int keyType;
  93. /**
  94. * The reference type for values. Must be HARD, SOFT, WEAK.
  95. * @serial
  96. */
  97. protected int valueType;
  98. /**
  99. * Should the value be automatically purged when the associated key has been collected?
  100. */
  101. protected boolean purgeValues;
  102. /**
  103. * ReferenceQueue used to eliminate stale mappings.
  104. * See purge.
  105. */
  106. private transient ReferenceQueue queue;
  107. //-----------------------------------------------------------------------
  108. /**
  109. * Constructor used during deserialization.
  110. */
  111. protected AbstractReferenceMap() {
  112. super();
  113. }
  114. /**
  115. * Constructs a new empty map with the specified reference types,
  116. * load factor and initial capacity.
  117. *
  118. * @param keyType the type of reference to use for keys;
  119. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  120. * @param valueType the type of reference to use for values;
  121. * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
  122. * @param capacity the initial capacity for the map
  123. * @param loadFactor the load factor for the map
  124. * @param purgeValues should the value be automatically purged when the
  125. * key is garbage collected
  126. */
  127. protected AbstractReferenceMap(
  128. int keyType, int valueType, int capacity,
  129. float loadFactor, boolean purgeValues) {
  130. super(capacity, loadFactor);
  131. verify("keyType", keyType);
  132. verify("valueType", valueType);
  133. this.keyType = keyType;
  134. this.valueType = valueType;
  135. this.purgeValues = purgeValues;
  136. }
  137. /**
  138. * Initialise this subclass during construction, cloning or deserialization.
  139. */
  140. protected void init() {
  141. queue = new ReferenceQueue();
  142. }
  143. //-----------------------------------------------------------------------
  144. /**
  145. * Checks the type int is a valid value.
  146. *
  147. * @param name the name for error messages
  148. * @param type the type value to check
  149. * @throws IllegalArgumentException if the value if invalid
  150. */
  151. private static void verify(String name, int type) {
  152. if ((type < HARD) || (type > WEAK)) {
  153. throw new IllegalArgumentException(name + " must be HARD, SOFT, WEAK.");
  154. }
  155. }
  156. //-----------------------------------------------------------------------
  157. /**
  158. * Gets the size of the map.
  159. *
  160. * @return the size
  161. */
  162. public int size() {
  163. purgeBeforeRead();
  164. return super.size();
  165. }
  166. /**
  167. * Checks whether the map is currently empty.
  168. *
  169. * @return true if the map is currently size zero
  170. */
  171. public boolean isEmpty() {
  172. purgeBeforeRead();
  173. return super.isEmpty();
  174. }
  175. /**
  176. * Checks whether the map contains the specified key.
  177. *
  178. * @param key the key to search for
  179. * @return true if the map contains the key
  180. */
  181. public boolean containsKey(Object key) {
  182. purgeBeforeRead();
  183. Entry entry = getEntry(key);
  184. if (entry == null) {
  185. return false;
  186. }
  187. return (entry.getValue() != null);
  188. }
  189. /**
  190. * Checks whether the map contains the specified value.
  191. *
  192. * @param value the value to search for
  193. * @return true if the map contains the value
  194. */
  195. public boolean containsValue(Object value) {
  196. purgeBeforeRead();
  197. if (value == null) {
  198. return false;
  199. }
  200. return super.containsValue(value);
  201. }
  202. /**
  203. * Gets the value mapped to the key specified.
  204. *
  205. * @param key the key
  206. * @return the mapped value, null if no match
  207. */
  208. public Object get(Object key) {
  209. purgeBeforeRead();
  210. Entry entry = getEntry(key);
  211. if (entry == null) {
  212. return null;
  213. }
  214. return entry.getValue();
  215. }
  216. /**
  217. * Puts a key-value mapping into this map.
  218. * Neither the key nor the value may be null.
  219. *
  220. * @param key the key to add, must not be null
  221. * @param value the value to add, must not be null
  222. * @return the value previously mapped to this key, null if none
  223. * @throws NullPointerException if either the key or value is null
  224. */
  225. public Object put(Object key, Object value) {
  226. if (key == null) {
  227. throw new NullPointerException("null keys not allowed");
  228. }
  229. if (value == null) {
  230. throw new NullPointerException("null values not allowed");
  231. }
  232. purgeBeforeWrite();
  233. return super.put(key, value);
  234. }
  235. /**
  236. * Removes the specified mapping from this map.
  237. *
  238. * @param key the mapping to remove
  239. * @return the value mapped to the removed key, null if key not in map
  240. */
  241. public Object remove(Object key) {
  242. if (key == null) {
  243. return null;
  244. }
  245. purgeBeforeWrite();
  246. return super.remove(key);
  247. }
  248. /**
  249. * Clears this map.
  250. */
  251. public void clear() {
  252. super.clear();
  253. while (queue.poll() != null) {} // drain the queue
  254. }
  255. //-----------------------------------------------------------------------
  256. /**
  257. * Gets a MapIterator over the reference map.
  258. * The iterator only returns valid key/value pairs.
  259. *
  260. * @return a map iterator
  261. */
  262. public MapIterator mapIterator() {
  263. return new ReferenceMapIterator(this);
  264. }
  265. /**
  266. * Returns a set view of this map's entries.
  267. * An iterator returned entry is valid until <code>next()</code> is called again.
  268. * The <code>setValue()</code> method on the <code>toArray</code> entries has no effect.
  269. *
  270. * @return a set view of this map's entries
  271. */
  272. public Set entrySet() {
  273. if (entrySet == null) {
  274. entrySet = new ReferenceEntrySet(this);
  275. }
  276. return entrySet;
  277. }
  278. /**
  279. * Returns a set view of this map's keys.
  280. *
  281. * @return a set view of this map's keys
  282. */
  283. public Set keySet() {
  284. if (keySet == null) {
  285. keySet = new ReferenceKeySet(this);
  286. }
  287. return keySet;
  288. }
  289. /**
  290. * Returns a collection view of this map's values.
  291. *
  292. * @return a set view of this map's values
  293. */
  294. public Collection values() {
  295. if (values == null) {
  296. values = new ReferenceValues(this);
  297. }
  298. return values;
  299. }
  300. //-----------------------------------------------------------------------
  301. /**
  302. * Purges stale mappings from this map before read operations.
  303. * <p>
  304. * This implementation calls {@link #purge()} to maintain a consistent state.
  305. */
  306. protected void purgeBeforeRead() {
  307. purge();
  308. }
  309. /**
  310. * Purges stale mappings from this map before write operations.
  311. * <p>
  312. * This implementation calls {@link #purge()} to maintain a consistent state.
  313. */
  314. protected void purgeBeforeWrite() {
  315. purge();
  316. }
  317. /**
  318. * Purges stale mappings from this map.
  319. * <p>
  320. * Note that this method is not synchronized! Special
  321. * care must be taken if, for instance, you want stale
  322. * mappings to be removed on a periodic basis by some
  323. * background thread.
  324. */
  325. protected void purge() {
  326. Reference ref = queue.poll();
  327. while (ref != null) {
  328. purge(ref);
  329. ref = queue.poll();
  330. }
  331. }
  332. /**
  333. * Purges the specified reference.
  334. *
  335. * @param ref the reference to purge
  336. */
  337. protected void purge(Reference ref) {
  338. // The hashCode of the reference is the hashCode of the
  339. // mapping key, even if the reference refers to the
  340. // mapping value...
  341. int hash = ref.hashCode();
  342. int index = hashIndex(hash, data.length);
  343. HashEntry previous = null;
  344. HashEntry entry = data[index];
  345. while (entry != null) {
  346. if (((ReferenceEntry) entry).purge(ref)) {
  347. if (previous == null) {
  348. data[index] = entry.next;
  349. } else {
  350. previous.next = entry.next;
  351. }
  352. this.size--;
  353. return;
  354. }
  355. previous = entry;
  356. entry = entry.next;
  357. }
  358. }
  359. //-----------------------------------------------------------------------
  360. /**
  361. * Gets the entry mapped to the key specified.
  362. *
  363. * @param key the key
  364. * @return the entry, null if no match
  365. */
  366. protected HashEntry getEntry(Object key) {
  367. if (key == null) {
  368. return null;
  369. } else {
  370. return super.getEntry(key);
  371. }
  372. }
  373. /**
  374. * Gets the hash code for a MapEntry.
  375. * Subclasses can override this, for example to use the identityHashCode.
  376. *
  377. * @param key the key to get a hash code for, may be null
  378. * @param value the value to get a hash code for, may be null
  379. * @return the hash code, as per the MapEntry specification
  380. */
  381. protected int hashEntry(Object key, Object value) {
  382. return (key == null ? 0 : key.hashCode()) ^
  383. (value == null ? 0 : value.hashCode());
  384. }
  385. /**
  386. * Compares two keys, in internal converted form, to see if they are equal.
  387. * <p>
  388. * This implementation converts the key from the entry to a real reference
  389. * before comparison.
  390. *
  391. * @param key1 the first key to compare passed in from outside
  392. * @param key2 the second key extracted from the entry via <code>entry.key</code>
  393. * @return true if equal
  394. */
  395. protected boolean isEqualKey(Object key1, Object key2) {
  396. key2 = (keyType > HARD ? ((Reference) key2).get() : key2);
  397. return (key1 == key2 || key1.equals(key2));
  398. }
  399. /**
  400. * Creates a ReferenceEntry instead of a HashEntry.
  401. *
  402. * @param next the next entry in sequence
  403. * @param hashCode the hash code to use
  404. * @param key the key to store
  405. * @param value the value to store
  406. * @return the newly created entry
  407. */
  408. protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) {
  409. return new ReferenceEntry(this, next, hashCode, key, value);
  410. }
  411. /**
  412. * Creates an entry set iterator.
  413. *
  414. * @return the entrySet iterator
  415. */
  416. protected Iterator createEntrySetIterator() {
  417. return new ReferenceEntrySetIterator(this);
  418. }
  419. /**
  420. * Creates an key set iterator.
  421. *
  422. * @return the keySet iterator
  423. */
  424. protected Iterator createKeySetIterator() {
  425. return new ReferenceKeySetIterator(this);
  426. }
  427. /**
  428. * Creates an values iterator.
  429. *
  430. * @return the values iterator
  431. */
  432. protected Iterator createValuesIterator() {
  433. return new ReferenceValuesIterator(this);
  434. }
  435. //-----------------------------------------------------------------------
  436. /**
  437. * EntrySet implementation.
  438. */
  439. static class ReferenceEntrySet extends EntrySet {
  440. protected ReferenceEntrySet(AbstractHashedMap parent) {
  441. super(parent);
  442. }
  443. public Object[] toArray() {
  444. return toArray(new Object[0]);
  445. }
  446. public Object[] toArray(Object[] arr) {
  447. // special implementation to handle disappearing entries
  448. ArrayList list = new ArrayList();
  449. Iterator iterator = iterator();
  450. while (iterator.hasNext()) {
  451. Entry e = (Entry) iterator.next();
  452. list.add(new DefaultMapEntry(e.getKey(), e.getValue()));
  453. }
  454. return list.toArray(arr);
  455. }
  456. }
  457. //-----------------------------------------------------------------------
  458. /**
  459. * KeySet implementation.
  460. */
  461. static class ReferenceKeySet extends KeySet {
  462. protected ReferenceKeySet(AbstractHashedMap parent) {
  463. super(parent);
  464. }
  465. public Object[] toArray() {
  466. return toArray(new Object[0]);
  467. }
  468. public Object[] toArray(Object[] arr) {
  469. // special implementation to handle disappearing keys
  470. List list = new ArrayList(parent.size());
  471. for (Iterator it = iterator(); it.hasNext(); ) {
  472. list.add(it.next());
  473. }
  474. return list.toArray(arr);
  475. }
  476. }
  477. //-----------------------------------------------------------------------
  478. /**
  479. * Values implementation.
  480. */
  481. static class ReferenceValues extends Values {
  482. protected ReferenceValues(AbstractHashedMap parent) {
  483. super(parent);
  484. }
  485. public Object[] toArray() {
  486. return toArray(new Object[0]);
  487. }
  488. public Object[] toArray(Object[] arr) {
  489. // special implementation to handle disappearing values
  490. List list = new ArrayList(parent.size());
  491. for (Iterator it = iterator(); it.hasNext(); ) {
  492. list.add(it.next());
  493. }
  494. return list.toArray(arr);
  495. }
  496. }
  497. //-----------------------------------------------------------------------
  498. /**
  499. * A MapEntry implementation for the map.
  500. * <p>
  501. * If getKey() or getValue() returns null, it means
  502. * the mapping is stale and should be removed.
  503. *
  504. * @since Commons Collections 3.1
  505. */
  506. protected static class ReferenceEntry extends HashEntry {
  507. /** The parent map */
  508. protected final AbstractReferenceMap parent;
  509. /**
  510. * Creates a new entry object for the ReferenceMap.
  511. *
  512. * @param parent the parent map
  513. * @param next the next entry in the hash bucket
  514. * @param hashCode the hash code of the key
  515. * @param key the key
  516. * @param value the value
  517. */
  518. public ReferenceEntry(AbstractReferenceMap parent, HashEntry next, int hashCode, Object key, Object value) {
  519. super(next, hashCode, null, null);
  520. this.parent = parent;
  521. this.key = toReference(parent.keyType, key, hashCode);
  522. this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately
  523. }
  524. /**
  525. * Gets the key from the entry.
  526. * This method dereferences weak and soft keys and thus may return null.
  527. *
  528. * @return the key, which may be null if it was garbage collected
  529. */
  530. public Object getKey() {
  531. return (parent.keyType > HARD) ? ((Reference) key).get() : key;
  532. }
  533. /**
  534. * Gets the value from the entry.
  535. * This method dereferences weak and soft value and thus may return null.
  536. *
  537. * @return the value, which may be null if it was garbage collected
  538. */
  539. public Object getValue() {
  540. return (parent.valueType > HARD) ? ((Reference) value).get() : value;
  541. }
  542. /**
  543. * Sets the value of the entry.
  544. *
  545. * @param obj the object to store
  546. * @return the previous value
  547. */
  548. public Object setValue(Object obj) {
  549. Object old = getValue();
  550. if (parent.valueType > HARD) {
  551. ((Reference)value).clear();
  552. }
  553. value = toReference(parent.valueType, obj, hashCode);
  554. return old;
  555. }
  556. /**
  557. * Compares this map entry to another.
  558. * <p>
  559. * This implementation uses <code>isEqualKey</code> and
  560. * <code>isEqualValue</code> on the main map for comparison.
  561. *
  562. * @param obj the other map entry to compare to
  563. * @return true if equal, false if not
  564. */
  565. public boolean equals(Object obj) {
  566. if (obj == this) {
  567. return true;
  568. }
  569. if (obj instanceof Map.Entry == false) {
  570. return false;
  571. }
  572. Map.Entry entry = (Map.Entry)obj;
  573. Object entryKey = entry.getKey(); // convert to hard reference
  574. Object entryValue = entry.getValue(); // convert to hard reference
  575. if ((entryKey == null) || (entryValue == null)) {
  576. return false;
  577. }
  578. // compare using map methods, aiding identity subclass
  579. // note that key is direct access and value is via method
  580. return parent.isEqualKey(entryKey, key) &&
  581. parent.isEqualValue(entryValue, getValue());
  582. }
  583. /**
  584. * Gets the hashcode of the entry using temporary hard references.
  585. * <p>
  586. * This implementation uses <code>hashEntry</code> on the main map.
  587. *
  588. * @return the hashcode of the entry
  589. */
  590. public int hashCode() {
  591. return parent.hashEntry(getKey(), getValue());
  592. }
  593. /**
  594. * Constructs a reference of the given type to the given referent.
  595. * The reference is registered with the queue for later purging.
  596. *
  597. * @param type HARD, SOFT or WEAK
  598. * @param referent the object to refer to
  599. * @param hash the hash code of the <i>key</i> of the mapping;
  600. * this number might be different from referent.hashCode() if
  601. * the referent represents a value and not a key
  602. */
  603. protected Object toReference(int type, Object referent, int hash) {
  604. switch (type) {
  605. case HARD: return referent;
  606. case SOFT: return new SoftRef(hash, referent, parent.queue);
  607. case WEAK: return new WeakRef(hash, referent, parent.queue);
  608. default: throw new Error();
  609. }
  610. }
  611. /**
  612. * Purges the specified reference
  613. * @param ref the reference to purge
  614. * @return true or false
  615. */
  616. boolean purge(Reference ref) {
  617. boolean r = (parent.keyType > HARD) && (key == ref);
  618. r = r || ((parent.valueType > HARD) && (value == ref));
  619. if (r) {
  620. if (parent.keyType > HARD) {
  621. ((Reference)key).clear();
  622. }
  623. if (parent.valueType > HARD) {
  624. ((Reference)value).clear();
  625. } else if (parent.purgeValues) {
  626. value = null;
  627. }
  628. }
  629. return r;
  630. }
  631. /**
  632. * Gets the next entry in the bucket.
  633. *
  634. * @return the next entry in the bucket
  635. */
  636. protected ReferenceEntry next() {
  637. return (ReferenceEntry) next;
  638. }
  639. }
  640. //-----------------------------------------------------------------------
  641. /**
  642. * The EntrySet iterator.
  643. */
  644. static class ReferenceEntrySetIterator implements Iterator {
  645. /** The parent map */
  646. final AbstractReferenceMap parent;
  647. // These fields keep track of where we are in the table.
  648. int index;
  649. ReferenceEntry entry;
  650. ReferenceEntry previous;
  651. // These Object fields provide hard references to the
  652. // current and next entry; this assures that if hasNext()
  653. // returns true, next() will actually return a valid element.
  654. Object nextKey, nextValue;
  655. Object currentKey, currentValue;
  656. int expectedModCount;
  657. public ReferenceEntrySetIterator(AbstractReferenceMap parent) {
  658. super();
  659. this.parent = parent;
  660. index = (parent.size() != 0 ? parent.data.length : 0);
  661. // have to do this here! size() invocation above
  662. // may have altered the modCount.
  663. expectedModCount = parent.modCount;
  664. }
  665. public boolean hasNext() {
  666. checkMod();
  667. while (nextNull()) {
  668. ReferenceEntry e = entry;
  669. int i = index;
  670. while ((e == null) && (i > 0)) {
  671. i--;
  672. e = (ReferenceEntry) parent.data[i];
  673. }
  674. entry = e;
  675. index = i;
  676. if (e == null) {
  677. currentKey = null;
  678. currentValue = null;
  679. return false;
  680. }
  681. nextKey = e.getKey();
  682. nextValue = e.getValue();
  683. if (nextNull()) {
  684. entry = entry.next();
  685. }
  686. }
  687. return true;
  688. }
  689. private void checkMod() {
  690. if (parent.modCount != expectedModCount) {
  691. throw new ConcurrentModificationException();
  692. }
  693. }
  694. private boolean nextNull() {
  695. return (nextKey == null) || (nextValue == null);
  696. }
  697. protected ReferenceEntry nextEntry() {
  698. checkMod();
  699. if (nextNull() && !hasNext()) {
  700. throw new NoSuchElementException();
  701. }
  702. previous = entry;
  703. entry = entry.next();
  704. currentKey = nextKey;
  705. currentValue = nextValue;
  706. nextKey = null;
  707. nextValue = null;
  708. return previous;
  709. }
  710. protected ReferenceEntry currentEntry() {
  711. checkMod();
  712. return previous;
  713. }
  714. public Object next() {
  715. return nextEntry();
  716. }
  717. public void remove() {
  718. checkMod();
  719. if (previous == null) {
  720. throw new IllegalStateException();
  721. }
  722. parent.remove(currentKey);
  723. previous = null;
  724. currentKey = null;
  725. currentValue = null;
  726. expectedModCount = parent.modCount;
  727. }
  728. }
  729. /**
  730. * The keySet iterator.
  731. */
  732. static class ReferenceKeySetIterator extends ReferenceEntrySetIterator {
  733. ReferenceKeySetIterator(AbstractReferenceMap parent) {
  734. super(parent);
  735. }
  736. public Object next() {
  737. return nextEntry().getKey();
  738. }
  739. }
  740. /**
  741. * The values iterator.
  742. */
  743. static class ReferenceValuesIterator extends ReferenceEntrySetIterator {
  744. ReferenceValuesIterator(AbstractReferenceMap parent) {
  745. super(parent);
  746. }
  747. public Object next() {
  748. return nextEntry().getValue();
  749. }
  750. }
  751. /**
  752. * The MapIterator implementation.
  753. */
  754. static class ReferenceMapIterator extends ReferenceEntrySetIterator implements MapIterator {
  755. protected ReferenceMapIterator(AbstractReferenceMap parent) {
  756. super(parent);
  757. }
  758. public Object next() {
  759. return nextEntry().getKey();
  760. }
  761. public Object getKey() {
  762. HashEntry current = currentEntry();
  763. if (current == null) {
  764. throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
  765. }
  766. return current.getKey();
  767. }
  768. public Object getValue() {
  769. HashEntry current = currentEntry();
  770. if (current == null) {
  771. throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
  772. }
  773. return current.getValue();
  774. }
  775. public Object setValue(Object value) {
  776. HashEntry current = currentEntry();
  777. if (current == null) {
  778. throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
  779. }
  780. return current.setValue(value);
  781. }
  782. }
  783. //-----------------------------------------------------------------------
  784. // These two classes store the hashCode of the key of
  785. // of the mapping, so that after they're dequeued a quick
  786. // lookup of the bucket in the table can occur.
  787. /**
  788. * A soft reference holder.
  789. */
  790. static class SoftRef extends SoftReference {
  791. /** the hashCode of the key (even if the reference points to a value) */
  792. private int hash;
  793. public SoftRef(int hash, Object r, ReferenceQueue q) {
  794. super(r, q);
  795. this.hash = hash;
  796. }
  797. public int hashCode() {
  798. return hash;
  799. }
  800. }
  801. /**
  802. * A weak reference holder.
  803. */
  804. static class WeakRef extends WeakReference {
  805. /** the hashCode of the key (even if the reference points to a value) */
  806. private int hash;
  807. public WeakRef(int hash, Object r, ReferenceQueue q) {
  808. super(r, q);
  809. this.hash = hash;
  810. }
  811. public int hashCode() {
  812. return hash;
  813. }
  814. }
  815. //-----------------------------------------------------------------------
  816. /**
  817. * Replaces the superclass method to store the state of this class.
  818. * <p>
  819. * Serialization is not one of the JDK's nicest topics. Normal serialization will
  820. * initialise the superclass before the subclass. Sometimes however, this isn't
  821. * what you want, as in this case the <code>put()</code> method on read can be
  822. * affected by subclass state.
  823. * <p>
  824. * The solution adopted here is to serialize the state data of this class in
  825. * this protected method. This method must be called by the
  826. * <code>writeObject()</code> of the first serializable subclass.
  827. * <p>
  828. * Subclasses may override if they have a specific field that must be present
  829. * on read before this implementation will work. Generally, the read determines
  830. * what must be serialized here, if anything.
  831. *
  832. * @param out the output stream
  833. */
  834. protected void doWriteObject(ObjectOutputStream out) throws IOException {
  835. out.writeInt(keyType);
  836. out.writeInt(valueType);
  837. out.writeBoolean(purgeValues);
  838. out.writeFloat(loadFactor);
  839. out.writeInt(data.length);
  840. for (MapIterator it = mapIterator(); it.hasNext();) {
  841. out.writeObject(it.next());
  842. out.writeObject(it.getValue());
  843. }
  844. out.writeObject(null); // null terminate map
  845. // do not call super.doWriteObject() as code there doesn't work for reference map
  846. }
  847. /**
  848. * Replaces the superclassm method to read the state of this class.
  849. * <p>
  850. * Serialization is not one of the JDK's nicest topics. Normal serialization will
  851. * initialise the superclass before the subclass. Sometimes however, this isn't
  852. * what you want, as in this case the <code>put()</code> method on read can be
  853. * affected by subclass state.
  854. * <p>
  855. * The solution adopted here is to deserialize the state data of this class in
  856. * this protected method. This method must be called by the
  857. * <code>readObject()</code> of the first serializable subclass.
  858. * <p>
  859. * Subclasses may override if the subclass has a specific field that must be present
  860. * before <code>put()</code> or <code>calculateThreshold()</code> will work correctly.
  861. *
  862. * @param in the input stream
  863. */
  864. protected void doReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  865. this.keyType = in.readInt();
  866. this.valueType = in.readInt();
  867. this.purgeValues = in.readBoolean();
  868. this.loadFactor = in.readFloat();
  869. int capacity = in.readInt();
  870. init();
  871. data = new HashEntry[capacity];
  872. while (true) {
  873. Object key = in.readObject();
  874. if (key == null) {
  875. break;
  876. }
  877. Object value = in.readObject();
  878. put(key, value);
  879. }
  880. threshold = calculateThreshold(data.length, loadFactor);
  881. // do not call super.doReadObject() as code there doesn't work for reference map
  882. }
  883. }