1 | /* |
2 | * Copyright (C) Christian Schulte, 2005-206 |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * |
9 | * o Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * |
12 | * o Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in |
14 | * the documentation and/or other materials provided with the |
15 | * distribution. |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
18 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
19 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, |
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | * |
28 | * $JOMC: WeakIdentityHashMap.java 4613 2012-09-22 10:07:08Z schulte $ |
29 | * |
30 | */ |
31 | package org.jomc.util; |
32 | |
33 | import java.lang.ref.ReferenceQueue; |
34 | import java.lang.ref.WeakReference; |
35 | import java.util.AbstractCollection; |
36 | import java.util.AbstractSet; |
37 | import java.util.Collection; |
38 | import java.util.ConcurrentModificationException; |
39 | import java.util.Iterator; |
40 | import java.util.Map; |
41 | import java.util.NoSuchElementException; |
42 | import java.util.Set; |
43 | |
44 | /** |
45 | * Hash-table based {@code Map} implementation with weak keys, using object-identity in place of object-equality when |
46 | * comparing keys. |
47 | * |
48 | * <p>In a {@code WeakIdentityHashMap} two keys {@code k1} and {@code k2} are considered equal if and only if |
49 | * {@code k1==k2} evaluates to {@code true}. An entry will automatically be removed when its key is no longer in |
50 | * ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded |
51 | * by the garbage collector, that is, made finalizable, finalised, and then reclaimed. When a key has been discarded its |
52 | * entry is effectively removed from the map, so this class behaves somewhat differently from other {@code Map} |
53 | * implementations and is not a general-purpose {@code Map} implementation! Although it implements the {@code Map} |
54 | * interface, it intentionally violates {@code Map}'s general contract, which mandates the use of the {@code equals} |
55 | * method when comparing objects.</p> |
56 | * |
57 | * <p>This class has performance characteristics similar to those of the {@code HashMap} class, and has the same |
58 | * efficiency parameters of {@code initialCapacity} and {@code loadFactor}. All of the optional map operations are |
59 | * provided. It does not support {@code null} values and the {@code null} key. All methods taking a key or value will |
60 | * throw a {@code NullPointerException} when given a {@code null} reference. No guarantees are made as to the order of |
61 | * the map; in particular, there is no guarantee that the order will remain constant over time. Like most collection |
62 | * classes, this class is not synchronised. A synchronised {@code WeakIdentityHashMap} may be constructed using the |
63 | * {@code Collections.synchronizedMap} method.</p> |
64 | * |
65 | * <p>The behaviour of the {@code WeakIdentityHashMap} class depends in part upon the actions of the garbage collector, |
66 | * so several {@code Map} invariants do not hold for this class. Because the garbage collector may discard keys at |
67 | * any time, a {@code WeakIdentityHashMap} may behave as though an unknown thread is silently removing entries. In |
68 | * particular, even if synchronising on a {@code WeakIdentityHashMap} instance and invoking none of its mutator |
69 | * methods, it is possible for the {@code size} method to return smaller values over time, for the {@code isEmpty} |
70 | * method to return {@code false} and then {@code true}, for the {@code containsKey} method to return {@code true} and |
71 | * later {@code false} for a given key, for the {@code get} method to return a value for a given key but later return |
72 | * {@code null}, for the {@code put} method to return {@code null} and the {@code remove} method to return {@code false} |
73 | * for a key that previously appeared to be in the map, and for successive examinations of the key set, the value |
74 | * collection, and the entry set to yield successively smaller numbers of elements. To protect against such situations |
75 | * all {@code Iterator}s and {@code Entry}s created by this class throw an {@code IllegalStateException} when a key |
76 | * becomes {@code null} or a mapping got removed.</p> |
77 | * |
78 | * <p>The iterators returned by the {@code iterator} method of the collections returned by all of this classes |
79 | * "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is |
80 | * created, in any way except through the iterator's own {@code remove} method, the iterator will throw a |
81 | * {@code ConcurrentModificationException}. Note that the fail-fast behaviour of an iterator cannot be guaranteed as it |
82 | * is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronised concurrent |
83 | * modification. Fail-fast iterators throw {@code ConcurrentModificationException}s on a best-effort basis. Therefore, |
84 | * it would be wrong to write a program that depends on this exception for its correctness: <i>the fail-fast behaviour |
85 | * of iterators should be used only to detect bugs.</i></p> |
86 | * |
87 | * @param <K> The type of keys maintained by this map. |
88 | * @param <V> The type of mapped values. |
89 | * |
90 | * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> |
91 | * @version $JOMC: WeakIdentityHashMap.java 4613 2012-09-22 10:07:08Z schulte $ |
92 | * |
93 | * @see java.util.HashMap |
94 | * @see java.util.WeakHashMap |
95 | * @see java.util.IdentityHashMap |
96 | * @see java.util.Collections#synchronizedMap |
97 | */ |
98 | public final class WeakIdentityHashMap<K, V> implements Map<K, V> |
99 | { |
100 | |
101 | /** Maximum capacity of the hash-table backing the implementation ({@code 2^30}). */ |
102 | private static final int MAXIMUM_CAPACITY = 0x40000000; |
103 | |
104 | /** Default initial capacity ({@code 2^4}). */ |
105 | private static final int DEFAULT_INITIAL_CAPACITY = 16; |
106 | |
107 | /** Default load factor ({@code 0.75}). */ |
108 | private static final float DEFAULT_LOAD_FACTOR = 0.75F; |
109 | |
110 | /** The number of times the map got structurally modified. */ |
111 | private int modifications; |
112 | |
113 | /** The number of mappings held by the map. */ |
114 | private int size; |
115 | |
116 | /** The size value at which the hash table needs resizing. */ |
117 | private int resizeThreshold; |
118 | |
119 | /** The hash-table backing the map. */ |
120 | private WeakEntry<K, V>[] hashTable; |
121 | |
122 | /** Queue, to which weak keys are appended to. */ |
123 | private final ReferenceQueue<K> referenceQueue = new ReferenceQueue<K>(); |
124 | |
125 | /** The key set view of the map. */ |
126 | private transient Set<K> keySet; |
127 | |
128 | /** The entry set view of the map. */ |
129 | private transient Set<Map.Entry<K, V>> entrySet; |
130 | |
131 | /** The value collection view of the map. */ |
132 | private transient Collection<V> valueCollection; |
133 | |
134 | /** The initial capacity of the hash table. */ |
135 | private final int initialCapacity; |
136 | |
137 | /** The load factor for the hash table. */ |
138 | private final float loadFactor; |
139 | |
140 | /** Null value returned by method {@link #getEntry(Object)}. */ |
141 | private final WeakEntry<K, V> NULL_ENTRY = new WeakEntry<K, V>( null, null, 0, this.referenceQueue ); |
142 | |
143 | /** |
144 | * Constructs a new, empty {@code WeakIdentityHashMap} with the default initial capacity ({@code 16}) and load |
145 | * factor ({@code 0.75}). |
146 | */ |
147 | public WeakIdentityHashMap() |
148 | { |
149 | this( DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR ); |
150 | } |
151 | |
152 | /** |
153 | * Constructs a new, empty {@code WeakIdentityHashMap} with the given initial capacity and the default load factor |
154 | * ({@code 0.75}). |
155 | * |
156 | * @param initialCapacity The initial capacity of the {@code WeakIdentityHashMap}. |
157 | * |
158 | * @throws IllegalArgumentException if {@code initialCapacity} is negative or greater than the maximum supported |
159 | * capacity ({@code 2^30}). |
160 | */ |
161 | public WeakIdentityHashMap( final int initialCapacity ) |
162 | { |
163 | this( initialCapacity, DEFAULT_LOAD_FACTOR ); |
164 | } |
165 | |
166 | /** |
167 | * Constructs a new, empty {@code WeakIdentityHashMap} with the default initial capacity ({@code 16}) and the given |
168 | * load factor. |
169 | * |
170 | * @param loadFactor The load factor of the {@code WeakIdentityHashMap}. |
171 | * |
172 | * @throws IllegalArgumentException if {@code loadFactor} is nonpositive. |
173 | */ |
174 | public WeakIdentityHashMap( final float loadFactor ) |
175 | { |
176 | this( DEFAULT_INITIAL_CAPACITY, loadFactor ); |
177 | } |
178 | |
179 | /** |
180 | * Constructs a new, empty {@code WeakIdentityHashMap} with the given initial capacity and the given load factor. |
181 | * |
182 | * @param initialCapacity The initial capacity of the {@code WeakIdentityHashMap}. |
183 | * @param loadFactor The load factor of the {@code WeakIdentityHashMap}. |
184 | * |
185 | * @throws IllegalArgumentException if {@code initialCapacity} is negative or greater than the maximum supported |
186 | * capacity ({@code 2^30}), or if {@code loadFactor} is nonpositive. |
187 | */ |
188 | public WeakIdentityHashMap( final int initialCapacity, final float loadFactor ) |
189 | { |
190 | if ( initialCapacity < 0 || initialCapacity > MAXIMUM_CAPACITY ) |
191 | { |
192 | throw new IllegalArgumentException( Integer.toString( initialCapacity ) ); |
193 | } |
194 | if ( loadFactor <= 0 || Float.isNaN( loadFactor ) ) |
195 | { |
196 | throw new IllegalArgumentException( Float.toString( loadFactor ) ); |
197 | } |
198 | |
199 | this.initialCapacity = initialCapacity; |
200 | this.loadFactor = loadFactor; |
201 | this.resizeThreshold = initialCapacity; |
202 | this.size = 0; |
203 | this.hashTable = new WeakEntry[ initialCapacity ]; |
204 | } |
205 | |
206 | /** |
207 | * Gets the number of key-value mappings in this map. |
208 | * <p>If the map contains more than {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}.</p> |
209 | * |
210 | * @return The number of key-value mappings in this map. |
211 | */ |
212 | public int size() |
213 | { |
214 | if ( this.size > 0 ) |
215 | { |
216 | this.purge(); |
217 | } |
218 | |
219 | return this.size; |
220 | } |
221 | |
222 | /** |
223 | * Gets a flag indicating if this map is empty. |
224 | * |
225 | * @return {@code true}, if this map contains no key-value mappings; {@code false}, if this map contains at least |
226 | * one mapping. |
227 | */ |
228 | public boolean isEmpty() |
229 | { |
230 | return this.size() == 0; |
231 | } |
232 | |
233 | /** |
234 | * Gets a flag indicating if this map contains a mapping for a given key. |
235 | * <p>More formally, returns {@code true}, if and only if this map contains a mapping for a key {@code k} such that |
236 | * {@code key==k}. There can be at most one such mapping.</p> |
237 | * |
238 | * @param key The key whose presence in this map is to be tested. |
239 | * |
240 | * @return {@code true}, if this map contains a mapping for {@code key}; {@code false}, if this map does not contain |
241 | * a mapping for {@code key}. |
242 | * |
243 | * @throws NullPointerException if {@code key} is {@code null}. |
244 | */ |
245 | public boolean containsKey( final Object key ) |
246 | { |
247 | if ( key == null ) |
248 | { |
249 | throw new NullPointerException( "key" ); |
250 | } |
251 | |
252 | return this.getEntry( key ).value != null; |
253 | } |
254 | |
255 | /** |
256 | * Gets a flag indicating if this map maps one or more keys to the specified value. |
257 | * <p>More formally, this method returns {@code true}, if and only if this map contains at least one mapping to a |
258 | * value {@code v} such that {@code value.equals(v)}. This operation requires time linear in the map size.</p> |
259 | * |
260 | * @param value The value whose presence in this map is to be tested. |
261 | * |
262 | * @return {@code true}, if this map maps one or more keys to {@code value}; {@code false}, if this map does not map |
263 | * any key to {@code value}. |
264 | * |
265 | * @throws NullPointerException if {@code value} is {@code null}. |
266 | */ |
267 | public boolean containsValue( final Object value ) |
268 | { |
269 | if ( value == null ) |
270 | { |
271 | throw new NullPointerException( "value" ); |
272 | } |
273 | |
274 | final WeakEntry<K, V>[] table = this.getHashTable(); |
275 | |
276 | for ( int i = table.length - 1; i >= 0; i-- ) |
277 | { |
278 | for ( WeakEntry<K, V> e = table[i]; e != null; e = e.next ) |
279 | { |
280 | if ( value.equals( e.value ) ) |
281 | { |
282 | return true; |
283 | } |
284 | } |
285 | } |
286 | |
287 | return false; |
288 | } |
289 | |
290 | /** |
291 | * Gets the value to which a given key is mapped or {@code null}, if this map contains no mapping for that key. |
292 | * <p>More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that |
293 | * {@code key==k}, then this method returns {@code v}; otherwise it returns {@code null}. There can be at most one |
294 | * such mapping.</p> |
295 | * |
296 | * @param key The key whose associated value is to be returned. |
297 | * |
298 | * @return The value to which {@code key} is mapped or {@code null}, if this map contains no mapping for |
299 | * {@code key}. |
300 | * |
301 | * @throws NullPointerException if {@code key} is {@code null}. |
302 | */ |
303 | public V get( final Object key ) |
304 | { |
305 | if ( key == null ) |
306 | { |
307 | throw new NullPointerException( "key" ); |
308 | } |
309 | |
310 | return this.getEntry( key ).value; |
311 | } |
312 | |
313 | /** |
314 | * Associates a given value with a given key in this map. |
315 | * <p>If the map previously contained a mapping for that key, the old value is replaced by the given value.</p> |
316 | * |
317 | * @param key The key with which {@code value} is to be associated. |
318 | * @param value The value to be associated with {@code key}. |
319 | * |
320 | * @return The value previously associated with {@code key} or {@code null}, if there was no mapping for |
321 | * {@code key}. |
322 | * |
323 | * @throws NullPointerException if {@code key} or {@code value} is {@code null}. |
324 | */ |
325 | public V put( final K key, final V value ) |
326 | { |
327 | if ( key == null ) |
328 | { |
329 | throw new NullPointerException( "key" ); |
330 | } |
331 | if ( value == null ) |
332 | { |
333 | throw new NullPointerException( "value" ); |
334 | } |
335 | |
336 | final int hashCode = System.identityHashCode( key ); |
337 | final WeakEntry<K, V>[] table = this.getHashTable(); |
338 | final int index = getHashTableIndex( hashCode, table.length ); |
339 | |
340 | for ( WeakEntry<K, V> e = table[index]; e != null; e = e.next ) |
341 | { |
342 | if ( e.hashCode == hashCode && e.get() == key ) |
343 | { |
344 | final V oldValue = e.value; |
345 | e.value = value; |
346 | return oldValue; |
347 | } |
348 | } |
349 | |
350 | final WeakEntry<K, V> entry = new WeakEntry<K, V>( key, value, hashCode, this.referenceQueue ); |
351 | entry.next = table[index]; |
352 | table[index] = entry; |
353 | |
354 | this.increaseSize(); |
355 | |
356 | return null; |
357 | } |
358 | |
359 | /** |
360 | * Removes the mapping for a given key from this map if it is present. |
361 | * <p>More formally, if this map contains a mapping from key {@code k} to value {@code v} such that {@code key==k}, |
362 | * that mapping is removed. The map can contain at most one such mapping. The map will not contain a mapping for the |
363 | * given key once the call returns.</p> |
364 | * |
365 | * @param key The key whose mapping is to be removed from the map. |
366 | * |
367 | * @return The value previously associated with {@code key} or {@code null}, if there was no mapping for |
368 | * {@code key}. |
369 | * |
370 | * @throws NullPointerException if {@code key} is {@code null}. |
371 | */ |
372 | public V remove( final Object key ) |
373 | { |
374 | if ( key == null ) |
375 | { |
376 | throw new NullPointerException( "key" ); |
377 | } |
378 | |
379 | final WeakEntry<K, V>[] table = this.getHashTable(); |
380 | final int hashCode = System.identityHashCode( key ); |
381 | final int index = getHashTableIndex( hashCode, table.length ); |
382 | |
383 | for ( WeakEntry<K, V> e = table[index], pre = null; e != null; pre = e, e = e.next ) |
384 | { |
385 | if ( e.hashCode == hashCode && e.get() == key ) |
386 | { |
387 | if ( pre != null ) |
388 | { |
389 | pre.next = e.next; |
390 | } |
391 | else |
392 | { |
393 | table[index] = e.next; |
394 | } |
395 | |
396 | this.decreaseSize(); |
397 | |
398 | final V removed = e.value; |
399 | e.removed = true; |
400 | e.value = null; |
401 | e.next = null; |
402 | return removed; |
403 | } |
404 | } |
405 | |
406 | return null; |
407 | } |
408 | |
409 | /** |
410 | * Copies all of the mappings from a given map to this map. |
411 | * <p>The effect of this call is equivalent to that of calling {@link #put(Object,Object) put(k, v)} on this map |
412 | * once for each mapping from key {@code k} to value {@code v} in the given map. The behavior of this operation is |
413 | * undefined if the given map is modified while the operation is in progress.</p> |
414 | * |
415 | * @param m The mappings to be stored in this map. |
416 | * |
417 | * @throws NullPointerException if {@code map} is {@code null}, or if {@code map} contains {@code null} keys or |
418 | * values. |
419 | */ |
420 | public void putAll( final Map<? extends K, ? extends V> m ) |
421 | { |
422 | if ( m == null ) |
423 | { |
424 | throw new NullPointerException( "m" ); |
425 | } |
426 | |
427 | for ( final Iterator<? extends Map.Entry<? extends K, ? extends V>> it = m.entrySet().iterator(); |
428 | it.hasNext(); ) |
429 | { |
430 | final Map.Entry<? extends K, ? extends V> entry = it.next(); |
431 | this.put( entry.getKey(), entry.getValue() ); |
432 | } |
433 | } |
434 | |
435 | /** Removes all of the mappings from this map so that the map will be empty after this call returns. */ |
436 | @SuppressWarnings( "empty-statement" ) |
437 | public void clear() |
438 | { |
439 | this.purge(); |
440 | this.hashTable = new WeakEntry[ this.initialCapacity ]; |
441 | this.size = 0; |
442 | this.resizeThreshold = this.initialCapacity; |
443 | this.modifications++; |
444 | while ( this.referenceQueue.poll() != null ); |
445 | } |
446 | |
447 | /** |
448 | * Gets a {@code Set} view of the keys contained in this map. |
449 | * <p>The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is |
450 | * modified while an iteration over the set is in progress (except through the iterator's own {@code remove} |
451 | * operation), the results of the iteration are undefined, that is, the iterator may throw an |
452 | * {@code IllegalStateException}. The set supports element removal, which removes the corresponding mapping from the |
453 | * map, via the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, {@code retainAll}, and {@code clear} |
454 | * operations. It does not support the {@code add} or {@code addAll} operations.</p> |
455 | * |
456 | * @return A set view of the keys contained in this map. |
457 | */ |
458 | public Set<K> keySet() |
459 | { |
460 | if ( this.keySet == null ) |
461 | { |
462 | this.keySet = new AbstractSet<K>() |
463 | { |
464 | |
465 | public Iterator<K> iterator() |
466 | { |
467 | return new KeyIterator(); |
468 | } |
469 | |
470 | public int size() |
471 | { |
472 | return WeakIdentityHashMap.this.size(); |
473 | } |
474 | |
475 | }; |
476 | |
477 | } |
478 | |
479 | return this.keySet; |
480 | } |
481 | |
482 | /** |
483 | * Gets a {@code Collection} view of the values contained in this map. |
484 | * <p>The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa. |
485 | * If the map is modified while an iteration over the collection is in progress (except through the iterator's own |
486 | * {@code remove} operation), the results of the iteration are undefined, that is, the iterator may throw an |
487 | * {@code IllegalStateException}. The collection supports element removal, which removes the corresponding mapping |
488 | * from the map, via the {@code Iterator.remove}, {@code Collection.remove}, {@code removeAll}, {@code retainAll} |
489 | * and {@code clear} operations. It does not support the {@code add} or {@code addAll} operations.</p> |
490 | * |
491 | * @return A collection view of the values contained in this map. |
492 | */ |
493 | public Collection<V> values() |
494 | { |
495 | if ( this.valueCollection == null ) |
496 | { |
497 | this.valueCollection = new AbstractCollection<V>() |
498 | { |
499 | |
500 | public Iterator<V> iterator() |
501 | { |
502 | return new ValueIterator(); |
503 | } |
504 | |
505 | public int size() |
506 | { |
507 | return WeakIdentityHashMap.this.size(); |
508 | } |
509 | |
510 | }; |
511 | } |
512 | |
513 | return this.valueCollection; |
514 | } |
515 | |
516 | /** |
517 | * Gets a {@code Set} view of the mappings contained in this map. |
518 | * <p>The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is |
519 | * modified while an iteration over the set is in progress (except through the iterator's own {@code remove} |
520 | * operation, or through the {@code setValue} operation on a map entry returned by the iterator) the results of the |
521 | * iteration are undefined, that is, the iterator may throw an {@code IllegalStateException}. The set supports |
522 | * element removal, which removes the corresponding mapping from the map, via the {@code Iterator.remove}, |
523 | * {@code Set.remove}, {@code removeAll}, {@code retainAll} and {@code clear} operations. It does not support the |
524 | * {@code add} or {@code addAll} operations.</p> |
525 | * |
526 | * @return A set view of the mappings contained in this map. |
527 | */ |
528 | public Set<Map.Entry<K, V>> entrySet() |
529 | { |
530 | if ( this.entrySet == null ) |
531 | { |
532 | this.entrySet = new AbstractSet<Map.Entry<K, V>>() |
533 | { |
534 | |
535 | public Iterator<Map.Entry<K, V>> iterator() |
536 | { |
537 | return new EntryIterator(); |
538 | } |
539 | |
540 | public int size() |
541 | { |
542 | return WeakIdentityHashMap.this.size(); |
543 | } |
544 | |
545 | }; |
546 | } |
547 | |
548 | return this.entrySet; |
549 | } |
550 | |
551 | /** |
552 | * Returns a string representation of the object. |
553 | * |
554 | * @return A string representation of the object. |
555 | */ |
556 | @Override |
557 | public String toString() |
558 | { |
559 | return super.toString() + this.internalString(); |
560 | } |
561 | |
562 | /** |
563 | * Compares the specified object with this map for equality. |
564 | * <p>Returns {@code true}, if the given object is also a map and the two maps represent the same mappings. More |
565 | * formally, two maps {@code m1} and {@code m2} represent the same mappings if |
566 | * {@code m1.entrySet().equals(m2.entrySet())}.</p> |
567 | * |
568 | * @param o The object to be compared for equality with this map. |
569 | * |
570 | * @return {@code true}, if {@code o} is equal to this map; {@code false}, if {@code o} is not equal to this map. |
571 | */ |
572 | @Override |
573 | public boolean equals( final Object o ) |
574 | { |
575 | boolean equal = this == o; |
576 | |
577 | if ( !equal && o instanceof Map<?, ?> ) |
578 | { |
579 | final Map<?, ?> that = (Map<?, ?>) o; |
580 | equal = this.entrySet().equals( that.entrySet() ); |
581 | } |
582 | |
583 | return equal; |
584 | } |
585 | |
586 | /** |
587 | * Gets the hash code value for this map. |
588 | * <p>The hash code of a map is defined to be the sum of the hash codes of each entry in the map's |
589 | * {@code entrySet()} view.</p> |
590 | * |
591 | * @return The hash code value for this map. |
592 | */ |
593 | @Override |
594 | public int hashCode() |
595 | { |
596 | return this.entrySet().hashCode(); |
597 | } |
598 | |
599 | /** |
600 | * Finalizes the object by polling the internal reference queue for any pending references. |
601 | * |
602 | * @since 1.2 |
603 | */ |
604 | @Override |
605 | protected void finalize() throws Throwable |
606 | { |
607 | this.modifications++; |
608 | while ( this.referenceQueue.poll() != null ); |
609 | super.finalize(); |
610 | } |
611 | |
612 | /** |
613 | * Creates a string representing the mappings of the instance. |
614 | * |
615 | * @return A string representing the mappings of the instance. |
616 | */ |
617 | private String internalString() |
618 | { |
619 | final StringBuilder buf = new StringBuilder( 12 * this.size() ).append( '{' ); |
620 | final WeakEntry<K, V>[] table = this.getHashTable(); |
621 | for ( int i = table.length - 1, index = 0; i >= 0; i-- ) |
622 | { |
623 | for ( WeakEntry<K, V> e = table[i]; e != null; e = e.next ) |
624 | { |
625 | if ( buf.length() > 1 ) |
626 | { |
627 | buf.append( ", " ); |
628 | } |
629 | |
630 | buf.append( '[' ).append( index++ ).append( "]=" ).append( e ); |
631 | } |
632 | } |
633 | |
634 | return buf.append( '}' ).toString(); |
635 | } |
636 | |
637 | /** |
638 | * Gets the index of a hash code value. |
639 | * |
640 | * @param hashCode The hash code value to return the index of. |
641 | * @param capacity The capacity to return an index for. |
642 | * |
643 | * @return The index of {@code hashCode} for {@code capacity}. |
644 | */ |
645 | private static int getHashTableIndex( final int hashCode, final int capacity ) |
646 | { |
647 | return hashCode & ( capacity - 1 ); |
648 | } |
649 | |
650 | /** |
651 | * Gets the hash-table backing the instance. |
652 | * <p>This method creates a new hash-table and re-hashes any mappings whenever the size of the map gets greater than |
653 | * the capacity of the internal hash-table times the load factor value.</p> |
654 | * |
655 | * @return The hash-table backing the instance. |
656 | */ |
657 | private WeakEntry<K, V>[] getHashTable() |
658 | { |
659 | if ( this.hashTable.length < this.resizeThreshold ) |
660 | { |
661 | @SuppressWarnings( "unchecked" ) |
662 | final WeakEntry<K, V>[] table = new WeakEntry[ this.calculateCapacity() ]; |
663 | |
664 | for ( int i = this.hashTable.length - 1; i >= 0; i-- ) |
665 | { |
666 | WeakEntry<K, V> entry = this.hashTable[i]; |
667 | |
668 | while ( entry != null ) |
669 | { |
670 | final WeakEntry<K, V> next = entry.next; |
671 | final int index = getHashTableIndex( entry.hashCode, table.length ); |
672 | |
673 | entry.next = table[index]; |
674 | table[index] = entry; |
675 | entry = next; |
676 | } |
677 | } |
678 | |
679 | this.hashTable = table; |
680 | this.modifications++; |
681 | } |
682 | |
683 | this.purge(); |
684 | return this.hashTable; |
685 | } |
686 | |
687 | /** Removes any garbage collected entries. */ |
688 | private void purge() |
689 | { |
690 | WeakEntry<K, V> purge; |
691 | |
692 | while ( ( purge = (WeakEntry<K, V>) this.referenceQueue.poll() ) != null ) |
693 | { |
694 | final int index = getHashTableIndex( purge.hashCode, this.hashTable.length ); |
695 | |
696 | for ( WeakEntry<K, V> e = this.hashTable[index], pre = null; e != null; pre = e, e = e.next ) |
697 | { |
698 | if ( e == purge ) |
699 | { |
700 | if ( pre != null ) |
701 | { |
702 | pre.next = purge.next; |
703 | } |
704 | else |
705 | { |
706 | this.hashTable[index] = purge.next; |
707 | } |
708 | |
709 | purge.removed = true; |
710 | purge.next = null; |
711 | purge.value = null; |
712 | |
713 | this.decreaseSize(); |
714 | |
715 | break; |
716 | } |
717 | } |
718 | } |
719 | } |
720 | |
721 | private void increaseSize() |
722 | { |
723 | if ( this.size < Integer.MAX_VALUE ) |
724 | { |
725 | this.size++; |
726 | this.resizeThreshold = (int) ( this.size * this.loadFactor ); |
727 | } |
728 | |
729 | this.modifications++; |
730 | } |
731 | |
732 | private void decreaseSize() |
733 | { |
734 | if ( this.size > 0 ) |
735 | { |
736 | this.size--; |
737 | } |
738 | |
739 | this.modifications++; |
740 | } |
741 | |
742 | private int calculateCapacity() |
743 | { |
744 | int maxCapacity = this.initialCapacity; |
745 | if ( maxCapacity < this.resizeThreshold ) |
746 | { |
747 | maxCapacity = this.resizeThreshold > MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY : this.resizeThreshold; |
748 | } |
749 | |
750 | int capacity = 1; |
751 | while ( capacity < maxCapacity ) |
752 | { |
753 | capacity <<= 1; |
754 | } |
755 | |
756 | return capacity; |
757 | } |
758 | |
759 | private WeakEntry<K, V> getEntry( final Object key ) |
760 | { |
761 | final int hashCode = System.identityHashCode( key ); |
762 | final WeakEntry<K, V>[] table = getHashTable(); |
763 | |
764 | for ( WeakEntry<K, V> e = table[getHashTableIndex( hashCode, table.length )]; e != null; e = e.next ) |
765 | { |
766 | if ( e.hashCode == hashCode && e.get() == key ) |
767 | { |
768 | return e; |
769 | } |
770 | } |
771 | |
772 | return NULL_ENTRY; |
773 | } |
774 | |
775 | /** |
776 | * A map entry (key-value pair) with weakly referenced key. |
777 | * <p>The {@code WeakIdentityHashMap.entrySet} method returns a collection-view of the map, whose elements are of |
778 | * this class. The only way to obtain a reference to a map entry is from the iterator of this collection-view. These |
779 | * {@code Map.Entry} objects are valid only for the duration of the iteration; more formally, the behavior of a map |
780 | * entry is undefined if the backing map has been modified after the entry was returned by the iterator, except |
781 | * through the {@code setValue} operation on the map entry.</p> |
782 | * |
783 | * @param <K> The type of the key. |
784 | * @param <V> The type of the value. |
785 | * |
786 | * @see WeakIdentityHashMap#entrySet() |
787 | */ |
788 | private static class WeakEntry<K, V> extends WeakReference<K> implements Map.Entry<K, V> |
789 | { |
790 | |
791 | /** The value of the entry. */ |
792 | private V value; |
793 | |
794 | /** The next entry in the bucket. */ |
795 | private WeakEntry<K, V> next; |
796 | |
797 | /** Flag indicating that this entry got removed from the map. */ |
798 | private boolean removed; |
799 | |
800 | /** The hash code value of the key. */ |
801 | private final int hashCode; |
802 | |
803 | WeakEntry( final K key, final V value, final int hashCode, final ReferenceQueue<K> queue ) |
804 | { |
805 | super( key, queue ); |
806 | this.hashCode = hashCode; |
807 | this.value = value; |
808 | } |
809 | |
810 | /** |
811 | * Gets the key corresponding to this entry. |
812 | * |
813 | * @return The key corresponding to this entry. |
814 | * |
815 | * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's |
816 | * {@code remove} operation or due to the key having been garbage collected). |
817 | */ |
818 | public K getKey() |
819 | { |
820 | final K key = this.get(); |
821 | |
822 | if ( key == null || this.removed ) |
823 | { |
824 | throw new IllegalStateException(); |
825 | } |
826 | |
827 | return key; |
828 | } |
829 | |
830 | /** |
831 | * Gets the value corresponding to this entry. |
832 | * |
833 | * @return The value corresponding to this entry. |
834 | * |
835 | * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's |
836 | * {@code remove} operation or due to the key having been garbage collected). |
837 | */ |
838 | public V getValue() |
839 | { |
840 | if ( this.get() == null || this.removed ) |
841 | { |
842 | throw new IllegalStateException(); |
843 | } |
844 | |
845 | return this.value; |
846 | } |
847 | |
848 | /** |
849 | * Replaces the value corresponding to this entry with the specified value. |
850 | * |
851 | * @param value The new value to be stored in this entry. |
852 | * |
853 | * @return The old value corresponding to the entry. |
854 | * |
855 | * @throws NullPointerException if {@code value} is {@code null}. |
856 | * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's |
857 | * {@code remove} operation or due to the key having been garbage collected). |
858 | */ |
859 | public V setValue( final V value ) |
860 | { |
861 | if ( value == null ) |
862 | { |
863 | throw new NullPointerException( "value" ); |
864 | } |
865 | if ( this.get() == null || this.removed ) |
866 | { |
867 | throw new IllegalStateException(); |
868 | } |
869 | |
870 | final V oldValue = this.getValue(); |
871 | |
872 | if ( value != oldValue && !value.equals( oldValue ) ) |
873 | { |
874 | this.value = value; |
875 | } |
876 | |
877 | return oldValue; |
878 | } |
879 | |
880 | /** |
881 | * Returns a string representation of the object. |
882 | * |
883 | * @return A string representation of the object. |
884 | */ |
885 | @Override |
886 | public String toString() |
887 | { |
888 | return super.toString() + this.internalString(); |
889 | } |
890 | |
891 | /** |
892 | * Compares a given object with this entry for equality. |
893 | * <p>Returns {@code true}, if the given object is also a map entry and the two entries represent the same |
894 | * mapping. More formally, two entries {@code e1} and {@code e2} represent the same mapping if |
895 | * <pre><blockquote> |
896 | * ( e1.getKey() == e2.getKey() ) && |
897 | * ( e1.getValue().equals( e2.getValue() ) ) |
898 | * </blockquote></pre></p> |
899 | * |
900 | * @param o The object to be compared for equality with this map entry. |
901 | * |
902 | * @return {@code true}, if {@code o} is equal to this map entry; {@code false}, if {@code o} is not equal to |
903 | * this map entry. |
904 | */ |
905 | @Override |
906 | public boolean equals( final Object o ) |
907 | { |
908 | boolean equal = this == o; |
909 | |
910 | if ( !equal && o instanceof Map.Entry<?, ?> ) |
911 | { |
912 | final Map.Entry<?, ?> that = (Map.Entry<?, ?>) o; |
913 | equal = this.getKey() == that.getKey() && this.getValue().equals( that.getValue() ); |
914 | } |
915 | |
916 | return equal; |
917 | } |
918 | |
919 | /** |
920 | * Gets the hash code value for this map entry. |
921 | * <p>The hash code of a map entry {@code e} is defined to be: |
922 | * <pre><blockquote> |
923 | * ( e.getKey() == null ? 0 : e.getKey().hashCode() ) ^ |
924 | * ( e.getValue() == null ? 0 : e.getValue().hashCode() ) |
925 | * </blockquote></pre></p> |
926 | * |
927 | * @return The hash code value for this map entry. |
928 | */ |
929 | @Override |
930 | public int hashCode() |
931 | { |
932 | return ( this.hashCode ) ^ ( this.getValue().hashCode() ); |
933 | } |
934 | |
935 | /** |
936 | * Creates a string representing the properties of the instance. |
937 | * |
938 | * @return A string representing the properties of the instance. |
939 | */ |
940 | private String internalString() |
941 | { |
942 | final StringBuilder buf = new StringBuilder( 50 ).append( '{' ); |
943 | buf.append( "key=" ).append( this.getKey() ).append( ", value=" ).append( this.getValue() ); |
944 | return buf.append( '}' ).toString(); |
945 | } |
946 | |
947 | } |
948 | |
949 | /** Base iterator implementation over the hash-table backing the implementation. */ |
950 | private class WeakEntryIterator |
951 | { |
952 | |
953 | /** The next element in the iteration. */ |
954 | private WeakEntry<K, V> next; |
955 | |
956 | /** The current element in the iteration. */ |
957 | private WeakEntry<K, V> current; |
958 | |
959 | /** The current index into the hash-table. */ |
960 | private int index; |
961 | |
962 | /** The number of modifications when this iterator got created. */ |
963 | private int modifications; |
964 | |
965 | /** Creates a new {@code WeakEntryIterator} instance. */ |
966 | WeakEntryIterator() |
967 | { |
968 | final WeakEntry<K, V>[] table = getHashTable(); |
969 | for ( this.index = table.length - 1; this.index >= 0; this.index-- ) |
970 | { |
971 | if ( table[this.index] != null ) |
972 | { |
973 | this.next = table[this.index--]; |
974 | break; |
975 | } |
976 | } |
977 | |
978 | this.modifications = WeakIdentityHashMap.this.modifications; |
979 | } |
980 | |
981 | /** |
982 | * Gets a flag indicating that the iteration has more elements. |
983 | * |
984 | * @return {@code true}, if the iterator has more elements; {@code false}, if the iterator does not have more |
985 | * elements. |
986 | */ |
987 | public boolean hasNext() |
988 | { |
989 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) |
990 | { |
991 | throw new ConcurrentModificationException(); |
992 | } |
993 | |
994 | return this.next != null; |
995 | } |
996 | |
997 | /** |
998 | * Gets the next element in the iteration. |
999 | * |
1000 | * @return The next element in the iteration. |
1001 | * |
1002 | * @throws NoSuchElementException if the iterator does not have more elements. |
1003 | */ |
1004 | public Map.Entry<K, V> nextElement() |
1005 | { |
1006 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) |
1007 | { |
1008 | throw new ConcurrentModificationException(); |
1009 | } |
1010 | if ( this.next == null ) |
1011 | { |
1012 | throw new NoSuchElementException(); |
1013 | } |
1014 | |
1015 | this.current = this.next; |
1016 | |
1017 | if ( this.next.next != null ) |
1018 | { |
1019 | this.next = this.next.next; |
1020 | } |
1021 | else |
1022 | { |
1023 | this.next = null; |
1024 | final WeakEntry<K, V>[] table = getHashTable(); |
1025 | for ( ; this.index >= 0; this.index-- ) |
1026 | { |
1027 | if ( table[this.index] != null ) |
1028 | { |
1029 | this.next = table[this.index--]; |
1030 | break; |
1031 | } |
1032 | } |
1033 | } |
1034 | |
1035 | return this.current; |
1036 | } |
1037 | |
1038 | /** |
1039 | * Removes from the underlying hash-table the last element returned by the iterator. |
1040 | * |
1041 | * @throws IllegalStateException if the {@code next} method has not yet been called, or the {@code remove} |
1042 | * method has already been called after the last call to the {@code next} method. |
1043 | */ |
1044 | public void remove() |
1045 | { |
1046 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) |
1047 | { |
1048 | throw new ConcurrentModificationException(); |
1049 | } |
1050 | if ( this.current == null ) |
1051 | { |
1052 | throw new IllegalStateException(); |
1053 | } |
1054 | |
1055 | final K key = this.current.getKey(); |
1056 | |
1057 | if ( key == null ) |
1058 | { |
1059 | throw new IllegalStateException(); |
1060 | } |
1061 | |
1062 | WeakIdentityHashMap.this.remove( key ); |
1063 | this.modifications = WeakIdentityHashMap.this.modifications; |
1064 | this.current = null; |
1065 | } |
1066 | |
1067 | } |
1068 | |
1069 | /** Iterator over the hash-table backing the implementation. */ |
1070 | private class EntryIterator extends WeakEntryIterator implements Iterator<Map.Entry<K, V>> |
1071 | { |
1072 | |
1073 | /** Creates a new {@code EntryIterator} instance. */ |
1074 | EntryIterator() |
1075 | { |
1076 | super(); |
1077 | } |
1078 | |
1079 | /** |
1080 | * Gets the next element in the iteration. |
1081 | * |
1082 | * @return The next element in the iteration. |
1083 | * |
1084 | * @throws NoSuchElementException if the iterator does not have more elements. |
1085 | */ |
1086 | public Map.Entry<K, V> next() |
1087 | { |
1088 | return super.nextElement(); |
1089 | } |
1090 | |
1091 | } |
1092 | |
1093 | /** Iterator over the hash-table backing the implementation. */ |
1094 | private class KeyIterator extends WeakEntryIterator implements Iterator<K> |
1095 | { |
1096 | |
1097 | /** Creates a new {@code KeyIterator} instance. */ |
1098 | KeyIterator() |
1099 | { |
1100 | super(); |
1101 | } |
1102 | |
1103 | /** |
1104 | * Gets the next element in the iteration. |
1105 | * |
1106 | * @return The next element in the iteration. |
1107 | * |
1108 | * @throws NoSuchElementException if the iterator does not have more elements. |
1109 | */ |
1110 | public K next() |
1111 | { |
1112 | return super.nextElement().getKey(); |
1113 | } |
1114 | |
1115 | } |
1116 | |
1117 | /** Iterator over the hash-table backing the implementation. */ |
1118 | private class ValueIterator extends WeakEntryIterator implements Iterator<V> |
1119 | { |
1120 | |
1121 | /** Creates a new {@code ValueIterator} instance. */ |
1122 | ValueIterator() |
1123 | { |
1124 | super(); |
1125 | } |
1126 | |
1127 | /** |
1128 | * Gets the next element in the iteration. |
1129 | * |
1130 | * @return The next element in the iteration. |
1131 | * |
1132 | * @throws NoSuchElementException if the iterator does not have more elements. |
1133 | */ |
1134 | public V next() |
1135 | { |
1136 | return super.nextElement().getValue(); |
1137 | } |
1138 | |
1139 | } |
1140 | |
1141 | } |