1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections4.map;
18
19 import java.io.IOException;
20 import java.io.InvalidObjectException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.lang.ref.Reference;
24 import java.lang.ref.ReferenceQueue;
25 import java.lang.ref.SoftReference;
26 import java.lang.ref.WeakReference;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.ConcurrentModificationException;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.NoSuchElementException;
34 import java.util.Objects;
35 import java.util.Set;
36
37 import org.apache.commons.collections4.MapIterator;
38 import org.apache.commons.collections4.keyvalue.DefaultMapEntry;
39
40 /**
41 * An abstract implementation of a hash-based map that allows the entries to
42 * be removed by the garbage collector.
43 * <p>
44 * This class implements all the features necessary for a subclass reference
45 * hash-based map. Key-value entries are stored in instances of the
46 * {@code ReferenceEntry} class which can be overridden and replaced.
47 * The iterators can similarly be replaced, without the need to replace the KeySet,
48 * EntrySet and Values view classes.
49 * </p>
50 * <p>
51 * Overridable methods are provided to change the default hashing behavior, and
52 * to change how entries are added to and removed from the map. Hopefully, all you
53 * need for unusual subclasses is here.
54 * </p>
55 * <p>
56 * When you construct an {@code AbstractReferenceMap}, you can specify what
57 * kind of references are used to store the map's keys and values.
58 * If non-hard references are used, then the garbage collector can remove
59 * mappings if a key or value becomes unreachable, or if the JVM's memory is
60 * running low. For information on how the different reference types behave,
61 * see {@link Reference}.
62 * </p>
63 * <p>
64 * Different types of references can be specified for keys and values.
65 * The keys can be configured to be weak but the values hard,
66 * in which case this class will behave like a
67 * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/WeakHashMap.html">
68 * {@code WeakHashMap}</a>. However, you can also specify hard keys and
69 * weak values, or any other combination. The default constructor uses
70 * hard keys and soft values, providing a memory-sensitive cache.
71 * </p>
72 * <p>
73 * This {@link Map} implementation does <em>not</em> allow null elements.
74 * Attempting to add a null key or value to the map will raise a
75 * {@code NullPointerException}.
76 * </p>
77 * <p>
78 * All the available iterators can be reset back to the start by casting to
79 * {@code ResettableIterator} and calling {@code reset()}.
80 * </p>
81 * <p>
82 * This implementation is not synchronized.
83 * You can use {@link java.util.Collections#synchronizedMap} to
84 * provide synchronized access to a {@code ReferenceMap}.
85 * </p>
86 *
87 * @param <K> The type of the keys in this map
88 * @param <V> The type of the values in this map
89 * @see java.lang.ref.Reference
90 * @since 3.1 (extracted from ReferenceMap in 3.0)
91 */
92 public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V> {
93
94 /**
95 * Base iterator class.
96 */
97 static class ReferenceBaseIterator<K, V> {
98
99 /** The parent map */
100 final AbstractReferenceMap<K, V> parent;
101
102 // These fields keep track of where we are in the table.
103 int index;
104 ReferenceEntry<K, V> next;
105 ReferenceEntry<K, V> current;
106
107 // These Object fields provide hard references to the
108 // current and next entry; this assures that if hasNext()
109 // returns true, next() will actually return a valid element.
110 K currentKey;
111 K nextKey;
112 V currentValue;
113 V nextValue;
114
115 int expectedModCount;
116
117 ReferenceBaseIterator(final AbstractReferenceMap<K, V> parent) {
118 this.parent = parent;
119 index = !parent.isEmpty() ? parent.data.length : 0;
120 // have to do this here! size() invocation above
121 // may have altered the modCount.
122 expectedModCount = parent.modCount;
123 }
124
125 private void checkMod() {
126 if (parent.modCount != expectedModCount) {
127 throw new ConcurrentModificationException();
128 }
129 }
130
131 protected ReferenceEntry<K, V> currentEntry() {
132 checkMod();
133 return current;
134 }
135
136 public boolean hasNext() {
137 checkMod();
138 while (nextNull()) {
139 ReferenceEntry<K, V> e = next;
140 int i = index;
141 while (e == null && i > 0) {
142 i--;
143 e = (ReferenceEntry<K, V>) parent.data[i];
144 }
145 next = e;
146 index = i;
147 if (e == null) {
148 return false;
149 }
150 nextKey = e.getKey();
151 nextValue = e.getValue();
152 if (nextNull()) {
153 next = next.next();
154 }
155 }
156 return true;
157 }
158
159 protected ReferenceEntry<K, V> nextEntry() {
160 checkMod();
161 if (nextNull() && !hasNext()) {
162 throw new NoSuchElementException();
163 }
164 current = next;
165 next = next.next();
166 currentKey = nextKey;
167 currentValue = nextValue;
168 nextKey = null;
169 nextValue = null;
170 return current;
171 }
172
173 private boolean nextNull() {
174 return nextKey == null || nextValue == null;
175 }
176
177 public void remove() {
178 checkMod();
179 if (current == null) {
180 throw new IllegalStateException();
181 }
182 parent.remove(currentKey);
183 current = null;
184 currentKey = null;
185 currentValue = null;
186 expectedModCount = parent.modCount;
187 }
188 }
189
190 /**
191 * A MapEntry implementation for the map.
192 * <p>
193 * If getKey() or getValue() returns null, it means
194 * the mapping is stale and should be removed.
195 * </p>
196 *
197 * @param <K> The type of the keys
198 * @param <V> The type of the values
199 * @since 3.1
200 */
201 protected static class ReferenceEntry<K, V> extends HashEntry<K, V> {
202
203 /** The parent map */
204 private final AbstractReferenceMap<K, V> parent;
205
206 /**
207 * Creates a new entry object for the ReferenceMap.
208 *
209 * @param parent The parent map
210 * @param next The next entry in the hash bucket
211 * @param hashCode The hash code of the key
212 * @param key The key
213 * @param value The value
214 */
215 public ReferenceEntry(final AbstractReferenceMap<K, V> parent, final HashEntry<K, V> next,
216 final int hashCode, final K key, final V value) {
217 super(next, hashCode, null, null);
218 this.parent = parent;
219 this.key = toReference(parent.keyType, key, hashCode);
220 this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately
221 }
222
223 /**
224 * Compares this map entry to another.
225 * <p>
226 * This implementation uses {@code isEqualKey} and
227 * {@code isEqualValue} on the main map for comparison.
228 * </p>
229 *
230 * @param obj The other map entry to compare to
231 * @return true if equal, false if not
232 */
233 @Override
234 public boolean equals(final Object obj) {
235 if (obj == this) {
236 return true;
237 }
238 if (!(obj instanceof Map.Entry)) {
239 return false;
240 }
241
242 final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
243 final Object entryKey = entry.getKey(); // convert to hard reference
244 final Object entryValue = entry.getValue(); // convert to hard reference
245 if (entryKey == null || entryValue == null) {
246 return false;
247 }
248 // compare using map methods, aiding identity subclass
249 // note that key is direct access and value is via method
250 return parent.isEqualKey(entryKey, key) &&
251 parent.isEqualValue(entryValue, getValue());
252 }
253
254 /**
255 * Gets the key from the entry.
256 * This method dereferences weak and soft keys and thus may return null.
257 *
258 * @return The key, which may be null if it was garbage collected
259 */
260 @Override
261 @SuppressWarnings("unchecked")
262 public K getKey() {
263 return (K) (parent.keyType == ReferenceStrength.HARD ? key : ((Reference<K>) key).get());
264 }
265
266 /**
267 * Gets the value from the entry.
268 * This method dereferences weak and soft value and thus may return null.
269 *
270 * @return The value, which may be null if it was garbage collected
271 */
272 @Override
273 @SuppressWarnings("unchecked")
274 public V getValue() {
275 return (V) (parent.valueType == ReferenceStrength.HARD ? value : ((Reference<V>) value).get());
276 }
277
278 /**
279 * Gets the hash code of the entry using temporary hard references.
280 * <p>
281 * This implementation uses {@code hashEntry} on the main map.
282 *
283 * @return The hash code of the entry
284 */
285 @Override
286 public int hashCode() {
287 return parent.hashEntry(getKey(), getValue());
288 }
289
290 /**
291 * Gets the next entry in the bucket.
292 *
293 * @return The next entry in the bucket
294 */
295 protected ReferenceEntry<K, V> next() {
296 return (ReferenceEntry<K, V>) next;
297 }
298
299 /**
300 * This method can be overridden to provide custom logic to purge value
301 */
302 protected void nullValue() {
303 value = null;
304 }
305
306 /**
307 * This is the callback for custom "after purge" logic
308 */
309 protected void onPurge() {
310 // empty
311 }
312
313 /**
314 * Purges the specified reference
315 *
316 * @param ref The reference to purge
317 * @return true or false
318 */
319 protected boolean purge(final Reference<?> ref) {
320 boolean r = parent.keyType != ReferenceStrength.HARD && key == ref;
321 r = r || parent.valueType != ReferenceStrength.HARD && value == ref;
322 if (r) {
323 if (parent.keyType != ReferenceStrength.HARD) {
324 ((Reference<?>) key).clear();
325 }
326 if (parent.valueType != ReferenceStrength.HARD) {
327 ((Reference<?>) value).clear();
328 } else if (parent.purgeValues) {
329 nullValue();
330 }
331 }
332 return r;
333 }
334
335 /**
336 * Sets the value of the entry.
337 *
338 * @param value The object to store
339 * @return The previous value
340 */
341 @Override
342 @SuppressWarnings("unchecked")
343 public V setValue(final V value) {
344 final V old = getValue();
345 if (parent.valueType != ReferenceStrength.HARD) {
346 ((Reference<V>) this.value).clear();
347 }
348 this.value = toReference(parent.valueType, value, hashCode);
349 return old;
350 }
351
352 /**
353 * Constructs a reference of the given type to the given referent.
354 * The reference is registered with the queue for later purging.
355 *
356 * @param <T> The type of the referenced object
357 * @param type HARD, SOFT or WEAK
358 * @param referent The object to refer to
359 * @param hash The hash code of the <em>key</em> of the mapping;
360 * this number might be different from referent.hashCode() if
361 * the referent represents a value and not a key
362 * @return The reference to the object
363 */
364 protected <T> Object toReference(final ReferenceStrength type, final T referent, final int hash) {
365 switch (type) {
366 case HARD:
367 return referent;
368 case SOFT:
369 return new SoftRef<>(hash, referent, parent.queue);
370 case WEAK:
371 return new WeakRef<>(hash, referent, parent.queue);
372 default:
373 break;
374 }
375 throw new IllegalArgumentException(type.toString());
376 }
377 }
378
379 /**
380 * EntrySet implementation.
381 */
382 static class ReferenceEntrySet<K, V> extends EntrySet<K, V> {
383
384 protected ReferenceEntrySet(final AbstractHashedMap<K, V> parent) {
385 super(parent);
386 }
387
388 @Override
389 public Object[] toArray() {
390 return toArray(new Object[size()]);
391 }
392
393 @Override
394 public <T> T[] toArray(final T[] arr) {
395 // special implementation to handle disappearing entries
396 final ArrayList<Map.Entry<K, V>> list = new ArrayList<>(size());
397 for (final Map.Entry<K, V> entry : this) {
398 list.add(new DefaultMapEntry<>(entry));
399 }
400 return list.toArray(arr);
401 }
402 }
403
404 /**
405 * The EntrySet iterator.
406 */
407 static class ReferenceEntrySetIterator<K, V>
408 extends ReferenceBaseIterator<K, V> implements Iterator<Map.Entry<K, V>> {
409
410 ReferenceEntrySetIterator(final AbstractReferenceMap<K, V> parent) {
411 super(parent);
412 }
413
414 @Override
415 public Map.Entry<K, V> next() {
416 return nextEntry();
417 }
418
419 }
420
421 /**
422 * KeySet implementation.
423 */
424 static class ReferenceKeySet<K> extends KeySet<K> {
425
426 protected ReferenceKeySet(final AbstractHashedMap<K, ?> parent) {
427 super(parent);
428 }
429
430 @Override
431 public Object[] toArray() {
432 return toArray(new Object[size()]);
433 }
434
435 @Override
436 public <T> T[] toArray(final T[] arr) {
437 // special implementation to handle disappearing keys
438 final List<K> list = new ArrayList<>(size());
439 forEach(list::add);
440 return list.toArray(arr);
441 }
442 }
443
444 /**
445 * The keySet iterator.
446 */
447 static class ReferenceKeySetIterator<K> extends ReferenceBaseIterator<K, Object> implements Iterator<K> {
448
449 @SuppressWarnings("unchecked")
450 ReferenceKeySetIterator(final AbstractReferenceMap<K, ?> parent) {
451 super((AbstractReferenceMap<K, Object>) parent);
452 }
453
454 @Override
455 public K next() {
456 return nextEntry().getKey();
457 }
458 }
459
460 /**
461 * The MapIterator implementation.
462 */
463 static class ReferenceMapIterator<K, V> extends ReferenceBaseIterator<K, V> implements MapIterator<K, V> {
464
465 protected ReferenceMapIterator(final AbstractReferenceMap<K, V> parent) {
466 super(parent);
467 }
468
469 @Override
470 public K getKey() {
471 final HashEntry<K, V> current = currentEntry();
472 if (current == null) {
473 throw new IllegalStateException(GETKEY_INVALID);
474 }
475 return current.getKey();
476 }
477
478 @Override
479 public V getValue() {
480 final HashEntry<K, V> current = currentEntry();
481 if (current == null) {
482 throw new IllegalStateException(GETVALUE_INVALID);
483 }
484 return current.getValue();
485 }
486
487 @Override
488 public K next() {
489 return nextEntry().getKey();
490 }
491
492 @Override
493 public V setValue(final V value) {
494 final HashEntry<K, V> current = currentEntry();
495 if (current == null) {
496 throw new IllegalStateException(SETVALUE_INVALID);
497 }
498 return current.setValue(value);
499 }
500 }
501
502 /**
503 * Enumerates reference types.
504 */
505 public enum ReferenceStrength {
506
507 /**
508 * Hard reference type.
509 */
510 HARD(0),
511
512 /**
513 * Soft reference type.
514 */
515 SOFT(1),
516
517 /**
518 * Weak reference type.
519 */
520 WEAK(2);
521
522 /**
523 * Resolve enum from int.
524 *
525 * @param value The int value
526 * @return ReferenceType
527 * @throws IllegalArgumentException if the specified value is invalid.
528 */
529 public static ReferenceStrength resolve(final int value) {
530 switch (value) {
531 case 0:
532 return HARD;
533 case 1:
534 return SOFT;
535 case 2:
536 return WEAK;
537 default:
538 throw new IllegalArgumentException();
539 }
540 }
541
542 /** Value */
543 public final int value;
544
545 ReferenceStrength(final int value) {
546 this.value = value;
547 }
548
549 }
550
551 /**
552 * Values implementation.
553 */
554 static class ReferenceValues<V> extends Values<V> {
555
556 protected ReferenceValues(final AbstractHashedMap<?, V> parent) {
557 super(parent);
558 }
559
560 @Override
561 public Object[] toArray() {
562 return toArray(new Object[size()]);
563 }
564
565 @Override
566 public <T> T[] toArray(final T[] arr) {
567 // special implementation to handle disappearing values
568 final List<V> list = new ArrayList<>(size());
569 forEach(list::add);
570 return list.toArray(arr);
571 }
572 }
573
574 /**
575 * The values iterator.
576 */
577 static class ReferenceValuesIterator<V> extends ReferenceBaseIterator<Object, V> implements Iterator<V> {
578
579 @SuppressWarnings("unchecked")
580 ReferenceValuesIterator(final AbstractReferenceMap<?, V> parent) {
581 super((AbstractReferenceMap<Object, V>) parent);
582 }
583
584 @Override
585 public V next() {
586 return nextEntry().getValue();
587 }
588 }
589
590 /**
591 * A soft reference holder.
592 */
593 static class SoftRef<T> extends SoftReference<T> {
594
595 /** The hashCode of the key (even if the reference points to a value) */
596 private final int hash;
597
598 SoftRef(final int hash, final T r, final ReferenceQueue<? super T> q) {
599 super(r, q);
600 this.hash = hash;
601 }
602
603 @Override
604 public boolean equals(final Object obj) {
605 if (this == obj) {
606 return true;
607 }
608 if (obj == null) {
609 return false;
610 }
611 if (getClass() != obj.getClass()) {
612 return false;
613 }
614 final SoftRef<?> other = (SoftRef<?>) obj;
615 return hash == other.hash;
616 }
617
618 @Override
619 public int hashCode() {
620 return hash;
621 }
622 }
623
624 /**
625 * A weak reference holder.
626 */
627 static class WeakRef<T> extends WeakReference<T> {
628
629 /** The hashCode of the key (even if the reference points to a value) */
630 private final int hash;
631
632 WeakRef(final int hash, final T r, final ReferenceQueue<? super T> q) {
633 super(r, q);
634 this.hash = hash;
635 }
636
637 @Override
638 public boolean equals(final Object obj) {
639 if (this == obj) {
640 return true;
641 }
642 if (obj == null) {
643 return false;
644 }
645 if (getClass() != obj.getClass()) {
646 return false;
647 }
648 final WeakRef<?> other = (WeakRef<?>) obj;
649 return hash == other.hash;
650 }
651
652 @Override
653 public int hashCode() {
654 return hash;
655 }
656 }
657
658 /**
659 * The reference type for keys.
660 */
661 private ReferenceStrength keyType;
662
663 /**
664 * The reference type for values.
665 */
666 private ReferenceStrength valueType;
667
668 /**
669 * Should the value be automatically purged when the associated key has been collected?
670 */
671 private boolean purgeValues;
672
673 /**
674 * ReferenceQueue used to eliminate stale mappings.
675 * See purge.
676 */
677 private transient ReferenceQueue<Object> queue;
678
679 /**
680 * Constructor used during deserialization.
681 */
682 protected AbstractReferenceMap() {
683 }
684
685 /**
686 * Constructs a new empty map with the specified reference types,
687 * load factor and initial capacity.
688 *
689 * @param keyType The type of reference to use for keys;
690 * must be {@link ReferenceStrength#HARD HARD},
691 * {@link ReferenceStrength#SOFT SOFT},
692 * {@link ReferenceStrength#WEAK WEAK}
693 * @param valueType The type of reference to use for values;
694 * must be {@link ReferenceStrength#HARD},
695 * {@link ReferenceStrength#SOFT SOFT},
696 * {@link ReferenceStrength#WEAK WEAK}
697 * @param capacity The initial capacity for the map
698 * @param loadFactor The load factor for the map
699 * @param purgeValues should the value be automatically purged when the
700 * key is garbage collected
701 */
702 protected AbstractReferenceMap(
703 final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity,
704 final float loadFactor, final boolean purgeValues) {
705 super(capacity, loadFactor);
706 this.keyType = keyType;
707 this.valueType = valueType;
708 this.purgeValues = purgeValues;
709 }
710
711 /**
712 * Clears this map.
713 */
714 @Override
715 public void clear() {
716 super.clear();
717 // Drain the queue
718 while (queue.poll() != null) { // NOPMD
719 }
720 }
721
722 /**
723 * Checks whether the map contains the specified key.
724 *
725 * @param key The key to search for
726 * @return true if the map contains the key
727 */
728 @Override
729 public boolean containsKey(final Object key) {
730 purgeBeforeRead();
731 final Entry<K, V> entry = getEntry(key);
732 if (entry == null) {
733 return false;
734 }
735 return entry.getValue() != null;
736 }
737
738 /**
739 * Checks whether the map contains the specified value.
740 *
741 * @param value The value to search for
742 * @return true if the map contains the value
743 */
744 @Override
745 public boolean containsValue(final Object value) {
746 purgeBeforeRead();
747 if (value == null) {
748 return false;
749 }
750 return super.containsValue(value);
751 }
752
753 /**
754 * Creates a ReferenceEntry instead of a HashEntry.
755 *
756 * @param next The next entry in sequence
757 * @param hashCode The hash code to use
758 * @param key The key to store
759 * @param value The value to store
760 * @return The newly created entry
761 */
762 @Override
763 protected ReferenceEntry<K, V> createEntry(final HashEntry<K, V> next, final int hashCode,
764 final K key, final V value) {
765 return new ReferenceEntry<>(this, next, hashCode, key, value);
766 }
767
768 /**
769 * Creates an entry set iterator.
770 *
771 * @return The entrySet iterator
772 */
773 @Override
774 protected Iterator<Map.Entry<K, V>> createEntrySetIterator() {
775 return new ReferenceEntrySetIterator<>(this);
776 }
777
778 /**
779 * Creates a key set iterator.
780 *
781 * @return The keySet iterator
782 */
783 @Override
784 protected Iterator<K> createKeySetIterator() {
785 return new ReferenceKeySetIterator<>(this);
786 }
787
788 /**
789 * Creates a values iterator.
790 *
791 * @return The values iterator
792 */
793 @Override
794 protected Iterator<V> createValuesIterator() {
795 return new ReferenceValuesIterator<>(this);
796 }
797
798 /**
799 * Replaces the superclass method to read the state of this class.
800 * <p>
801 * Serialization is not one of the JDK's nicest topics. Normal serialization will
802 * initialize the superclass before the subclass. Sometimes however, this isn't
803 * what you want, as in this case the {@code put()} method on read can be
804 * affected by subclass state.
805 * </p>
806 * <p>
807 * The solution adopted here is to deserialize the state data of this class in
808 * this protected method. This method must be called by the
809 * {@code readObject()} of the first serializable subclass.
810 * </p>
811 * <p>
812 * Subclasses may override if the subclass has a specific field that must be present
813 * before {@code put()} or {@code calculateThreshold()} will work correctly.
814 * </p>
815 *
816 * @param in The input stream
817 * @throws IOException Thrown if an error occurs while reading from the stream
818 * @throws ClassNotFoundException if an object read from the stream cannot be loaded
819 */
820 @Override
821 @SuppressWarnings("unchecked")
822 protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
823 keyType = ReferenceStrength.resolve(in.readInt());
824 valueType = ReferenceStrength.resolve(in.readInt());
825 purgeValues = in.readBoolean();
826 loadFactor = in.readFloat();
827 if (loadFactor <= 0.0f || Float.isNaN(loadFactor)) {
828 throw new InvalidObjectException("Load factor must be greater than 0");
829 }
830 final int capacity = in.readInt();
831 init();
832 data = new HashEntry[capacity];
833
834 // COLLECTIONS-599: Calculate threshold before populating, otherwise it will be 0
835 // when it hits AbstractHashedMap.checkCapacity() and so will unnecessarily
836 // double up the size of the "data" array during population.
837 //
838 // NB: AbstractHashedMap.doReadObject() DOES calculate the threshold before populating.
839 //
840 threshold = calculateThreshold(data.length, loadFactor);
841
842 while (true) {
843 final K key = (K) in.readObject();
844 if (key == null) {
845 break;
846 }
847 final V value = (V) in.readObject();
848 put(key, value);
849 }
850 // do not call super.doReadObject() as code there doesn't work for reference map
851 }
852
853 /**
854 * Replaces the superclass method to store the state of this class.
855 * <p>
856 * Serialization is not one of the JDK's nicest topics. Normal serialization will
857 * initialize the superclass before the subclass. Sometimes however, this isn't
858 * what you want, as in this case the {@code put()} method on read can be
859 * affected by subclass state.
860 * </p>
861 * <p>
862 * The solution adopted here is to serialize the state data of this class in
863 * this protected method. This method must be called by the
864 * {@code writeObject()} of the first serializable subclass.
865 * </p>
866 * <p>
867 * Subclasses may override if they have a specific field that must be present
868 * on read before this implementation will work. Generally, the read determines
869 * what must be serialized here, if anything.
870 * </p>
871 *
872 * @param out The output stream
873 * @throws IOException Thrown if an error occurs while writing to the stream
874 */
875 @Override
876 protected void doWriteObject(final ObjectOutputStream out) throws IOException {
877 out.writeInt(keyType.value);
878 out.writeInt(valueType.value);
879 out.writeBoolean(purgeValues);
880 out.writeFloat(loadFactor);
881 out.writeInt(data.length);
882 for (final MapIterator<K, V> it = mapIterator(); it.hasNext();) {
883 out.writeObject(it.next());
884 out.writeObject(it.getValue());
885 }
886 out.writeObject(null); // null terminate map
887 // do not call super.doWriteObject() as code there doesn't work for reference map
888 }
889
890 /**
891 * Returns a set view of this map's entries.
892 * An iterator returned entry is valid until {@code next()} is called again.
893 * The {@code setValue()} method on the {@code toArray} entries has no effect.
894 *
895 * @return A set view of this map's entries
896 */
897 @Override
898 public Set<Map.Entry<K, V>> entrySet() {
899 if (entrySet == null) {
900 entrySet = new ReferenceEntrySet<>(this);
901 }
902 return entrySet;
903 }
904
905 /**
906 * Gets the value mapped to the key specified.
907 *
908 * @param key The key
909 * @return The mapped value, null if no match
910 */
911 @Override
912 public V get(final Object key) {
913 purgeBeforeRead();
914 final Entry<K, V> entry = getEntry(key);
915 if (entry == null) {
916 return null;
917 }
918 return entry.getValue();
919 }
920
921 /**
922 * Gets the entry mapped to the key specified.
923 *
924 * @param key The key
925 * @return The entry, null if no match
926 */
927 @Override
928 protected HashEntry<K, V> getEntry(final Object key) {
929 if (key == null) {
930 return null;
931 }
932 return super.getEntry(key);
933 }
934
935 /**
936 * Gets the hash code for a MapEntry.
937 * Subclasses can override this, for example to use the identityHashCode.
938 *
939 * @param key The key to get a hash code for, may be null
940 * @param value The value to get a hash code for, may be null
941 * @return The hash code, as per the MapEntry specification
942 */
943 protected int hashEntry(final Object key, final Object value) {
944 return (key == null ? 0 : key.hashCode()) ^
945 (value == null ? 0 : value.hashCode());
946 }
947
948 /**
949 * Initialize this subclass during construction, cloning or deserialization.
950 */
951 @Override
952 protected void init() {
953 queue = new ReferenceQueue<>();
954 }
955
956 /**
957 * Checks whether the map is currently empty.
958 *
959 * @return true if the map is currently size zero
960 */
961 @Override
962 public boolean isEmpty() {
963 purgeBeforeRead();
964 return super.isEmpty();
965 }
966
967 /**
968 * Compares two keys, in internal converted form, to see if they are equal.
969 * <p>
970 * This implementation converts the key from the entry to a real reference
971 * before comparison.
972 * </p>
973 *
974 * @param key1 The first key to compare passed in from outside
975 * @param key2 The second key extracted from the entry via {@code entry.key}
976 * @return true if equal
977 */
978 @Override
979 @SuppressWarnings("unchecked")
980 protected boolean isEqualKey(final Object key1, Object key2) {
981 key2 = keyType == ReferenceStrength.HARD ? key2 : ((Reference<K>) key2).get();
982 return Objects.equals(key1, key2);
983 }
984
985 /**
986 * Provided protected read-only access to the key type.
987 *
988 * @param type The type to check against.
989 * @return true if keyType has the specified type
990 */
991 protected boolean isKeyType(final ReferenceStrength type) {
992 return keyType == type;
993 }
994
995 /**
996 * Provided protected read-only access to the value type.
997 *
998 * @param type The type to check against.
999 * @return true if valueType has the specified type
1000 */
1001 protected boolean isValueType(final ReferenceStrength type) {
1002 return valueType == type;
1003 }
1004
1005 /**
1006 * Returns a set view of this map's keys.
1007 *
1008 * @return A set view of this map's keys
1009 */
1010 @Override
1011 public Set<K> keySet() {
1012 if (keySet == null) {
1013 keySet = new ReferenceKeySet<>(this);
1014 }
1015 return keySet;
1016 }
1017
1018 /**
1019 * Gets a MapIterator over the reference map.
1020 * The iterator only returns valid key/value pairs.
1021 *
1022 * @return A map iterator
1023 */
1024 @Override
1025 public MapIterator<K, V> mapIterator() {
1026 return new ReferenceMapIterator<>(this);
1027 }
1028
1029 /**
1030 * Purges stale mappings from this map.
1031 * <p>
1032 * Note that this method is not synchronized! Special
1033 * care must be taken if, for instance, you want stale
1034 * mappings to be removed on a periodic basis by some
1035 * background thread.
1036 * </p>
1037 */
1038 protected void purge() {
1039 Reference<?> ref = queue.poll();
1040 while (ref != null) {
1041 purge(ref);
1042 ref = queue.poll();
1043 }
1044 }
1045
1046 /**
1047 * Purges the specified reference.
1048 *
1049 * @param ref The reference to purge
1050 */
1051 protected void purge(final Reference<?> ref) {
1052 // The hashCode of the reference is the hashCode of the
1053 // mapping key, even if the reference refers to the
1054 // mapping value...
1055 final int hash = ref.hashCode();
1056 final int index = hashIndex(hash, data.length);
1057 HashEntry<K, V> previous = null;
1058 HashEntry<K, V> entry = data[index];
1059 while (entry != null) {
1060 final ReferenceEntry<K, V> refEntry = (ReferenceEntry<K, V>) entry;
1061 if (refEntry.purge(ref)) {
1062 if (previous == null) {
1063 data[index] = entry.next;
1064 } else {
1065 previous.next = entry.next;
1066 }
1067 size--;
1068 refEntry.onPurge();
1069 return;
1070 }
1071 previous = entry;
1072 entry = entry.next;
1073 }
1074
1075 }
1076
1077 // These two classes store the hashCode of the key of
1078 // the mapping, so that after they're dequeued a quick
1079 // lookup of the bucket in the table can occur.
1080
1081 /**
1082 * Purges stale mappings from this map before read operations.
1083 * <p>
1084 * This implementation calls {@link #purge()} to maintain a consistent state.
1085 */
1086 protected void purgeBeforeRead() {
1087 purge();
1088 }
1089
1090 /**
1091 * Purges stale mappings from this map before write operations.
1092 * <p>
1093 * This implementation calls {@link #purge()} to maintain a consistent state.
1094 * </p>
1095 */
1096 protected void purgeBeforeWrite() {
1097 purge();
1098 }
1099
1100 /**
1101 * Puts a key-value mapping into this map.
1102 * Neither the key nor the value may be null.
1103 *
1104 * @param key The key to add, must not be null
1105 * @param value The value to add, must not be null
1106 * @return The value previously mapped to this key, null if none
1107 * @throws NullPointerException if either the key or value is null
1108 */
1109 @Override
1110 public V put(final K key, final V value) {
1111 Objects.requireNonNull(key, "key");
1112 Objects.requireNonNull(value, "value");
1113 purgeBeforeWrite();
1114 return super.put(key, value);
1115 }
1116
1117 /**
1118 * Removes the specified mapping from this map.
1119 *
1120 * @param key The mapping to remove
1121 * @return The value mapped to the removed key, null if key not in map
1122 */
1123 @Override
1124 public V remove(final Object key) {
1125 if (key == null) {
1126 return null;
1127 }
1128 purgeBeforeWrite();
1129 return super.remove(key);
1130 }
1131
1132 /**
1133 * Gets the size of the map.
1134 *
1135 * @return The size
1136 */
1137 @Override
1138 public int size() {
1139 purgeBeforeRead();
1140 return super.size();
1141 }
1142
1143 /**
1144 * Returns a collection view of this map's values.
1145 *
1146 * @return A set view of this map's values
1147 */
1148 @Override
1149 public Collection<V> values() {
1150 if (values == null) {
1151 values = new ReferenceValues<>(this);
1152 }
1153 return values;
1154 }
1155 }