001    /*
002     *   Copyright (C) Christian Schulte, 2005-206
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 4173 2012-01-15 07:50:51Z schulte2005 $
029     *
030     */
031    package org.jomc.util;
032    
033    import java.lang.ref.ReferenceQueue;
034    import java.lang.ref.WeakReference;
035    import java.util.AbstractCollection;
036    import java.util.AbstractSet;
037    import java.util.Collection;
038    import java.util.ConcurrentModificationException;
039    import java.util.Iterator;
040    import java.util.Map;
041    import java.util.NoSuchElementException;
042    import 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>In a {@code WeakIdentityHashMap} two keys {@code k1} and {@code k2} are considered equal if and only if
049     * {@code k1==k2} evaluates to {@code true}. An entry will automatically be removed when its key is no longer in
050     * ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded
051     * by the garbage collector, that is, made finalizable, finalised, and then reclaimed. When a key has been discarded its
052     * entry is effectively removed from the map, so this class behaves somewhat differently from other {@code Map}
053     * implementations and is not a general-purpose {@code Map} implementation! Although it implements the {@code Map}
054     * interface, it intentionally violates {@code Map}'s general contract, which mandates the use of the {@code equals}
055     * method when comparing objects.</p>
056     *
057     * <p>This class has performance characteristics similar to those of the {@code HashMap} class, and has the same
058     * efficiency parameters of {@code initialCapacity} and {@code loadFactor}. All of the optional map operations are
059     * provided. It does not support {@code null} values and the {@code null} key. All methods taking a key or value will
060     * throw a {@code NullPointerException} when given a {@code null} reference. No guarantees are made as to the order of
061     * the map; in particular, there is no guarantee that the order will remain constant over time. Like most collection
062     * classes, this class is not synchronised. A synchronised {@code WeakIdentityHashMap} may be constructed using the
063     * {@code Collections.synchronizedMap} method.</p>
064     *
065     * <p>The behaviour of the {@code WeakIdentityHashMap} class depends in part upon the actions of the garbage collector,
066     * so several {@code Map} invariants do not hold for this class. Because the garbage collector may discard keys at
067     * any time, a {@code WeakIdentityHashMap} may behave as though an unknown thread is silently removing entries. In
068     * particular, even if synchronising on a {@code WeakIdentityHashMap} instance and invoking none of its mutator
069     * methods, it is possible for the {@code size} method to return smaller values over time, for the {@code isEmpty}
070     * method to return {@code false} and then {@code true}, for the {@code containsKey} method to return {@code true} and
071     * later {@code false} for a given key, for the {@code get} method to return a value for a given key but later return
072     * {@code null}, for the {@code put} method to return {@code null} and the {@code remove} method to return {@code false}
073     * for a key that previously appeared to be in the map, and for successive examinations of the key set, the value
074     * collection, and the entry set to yield successively smaller numbers of elements. To protect against such situations
075     * all {@code Iterator}s and {@code Entry}s created by this class throw an {@code IllegalStateException} when a key
076     * becomes {@code null} or a mapping got removed.</p>
077     *
078     * <p>The iterators returned by the {@code iterator} method of the collections returned by all of this classes
079     * "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is
080     * created, in any way except through the iterator's own {@code remove} method, the iterator will throw a
081     * {@code ConcurrentModificationException}. Note that the fail-fast behaviour of an iterator cannot be guaranteed as it
082     * is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronised concurrent
083     * modification. Fail-fast iterators throw {@code ConcurrentModificationException}s on a best-effort basis. Therefore,
084     * it would be wrong to write a program that depends on this exception for its correctness: <i>the fail-fast behaviour
085     * of iterators should be used only to detect bugs.</i></p>
086     *
087     * @param <K> The type of keys maintained by this map.
088     * @param <V> The type of mapped values.
089     *
090     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
091     * @version $JOMC: WeakIdentityHashMap.java 4173 2012-01-15 07:50:51Z schulte2005 $
092     *
093     * @see java.util.HashMap
094     * @see java.util.WeakHashMap
095     * @see java.util.IdentityHashMap
096     * @see java.util.Collections#synchronizedMap
097     */
098    public final class WeakIdentityHashMap<K, V> implements Map<K, V>
099    {
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() )  &amp;&amp;
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    }