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