View Javadoc

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