KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > go > trove > util > IdentityMap


1 /* ====================================================================
2  * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group
3  * ====================================================================
4  * The Tea Software License, Version 1.1
5  *
6  * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Walt Disney Internet Group (http://opensource.go.com/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact opensource@dig.com.
31  *
32  * 5. Products derived from this software may not be called "Tea",
33  * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34  * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
35  * written permission of the Walt Disney Internet Group.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
41  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
43  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  * For more information about Tea, please see http://opensource.go.com/.
51  */

52
53 package com.go.trove.util;
54
55 import java.lang.ref.*;
56 import java.util.*;
57
58 /******************************************************************************
59  * An IdentityMap is like WeakHashMap, except it uses a key's identity
60  * hashcode and equals methods. IdentityMap is not thread-safe and must be
61  * wrapped with Collections.synchronizedMap to be made thread-safe. Most of the
62  * implementation for this class is ripped off from java.util.HashMap, but not
63  * java.util.WeakHashMap, in order to acheive greater efficiency.
64  * <p>
65  * The documentation for WeakHashMap states that it is intended primarily
66  * for use with key objects whose equals methods test for object identity
67  * using the == operator. Because WeakHashMap uses a key's own equals and
68  * hashcode methods, it is better suited for implementing methods that behave
69  * like {@link String#intern}. However, because WeakHashMap stongly references
70  * values, {@link Utils#intern Utils.intern} provides a safer intern mechanism.
71  * <p>
72  * In this implementation, all key objects are tested for equality using the
73  * == operator, and null keys are not permitted. IdentityMap is therefore
74  * better suited for "canonicalized" mappings.
75  * <p>
76  * Note: Weakly referenced entries may be automatically removed during
77  * either accessor or mutator operations, possibly causing a concurrent
78  * modification to be detected. Therefore, even if multiple threads are only
79  * accessing this map, be sure to synchronize this map first. Also, do not
80  * rely on the value returned by size() when using an iterator from this map.
81  * The iterators may return less entries than the amount reported by size().
82  *
83  * @author Brian S O'Neill
84  * @version
85  * <!--$$Revision:--> 19 <!-- $-->, <!--$$JustDate:--> 00/12/18 <!-- $-->
86  * @see java.util.WeakHashMap
87  * @see java.util.HashMap
88  */

89 public class IdentityMap extends AbstractMap implements Map, Cloneable JavaDoc {
90     // Types of Iterators
91
static final int KEYS = 0;
92     static final int VALUES = 1;
93     static final int ENTRIES = 2;
94
95     static final Iterator cEmptyHashIterator = new Iterator() {
96         public boolean hasNext() {
97             return false;
98         }
99         
100         public Object JavaDoc next() {
101             throw new NoSuchElementException();
102         }
103         
104         public void remove() {
105             throw new IllegalStateException JavaDoc();
106         }
107     };
108
109     /**
110      * Test program.
111      */

112     /*
113     public static void main(String[] args) throws Exception {
114         Map map = new IdentityMap();
115         map.put("Hello", "There");
116         for (int i=0; i<1000000; i++) {
117             if (i % 5 == 0) {
118                 map.put(new String("Hello"), "Dude");
119             }
120             map.get("Hello");
121             map.get("Stuff");
122         }
123
124         System.out.println(map.containsValue("Dude"));
125         System.out.println(map.get("Hello"));
126
127         System.gc();
128
129         System.out.println(map);
130         System.out.println(map.size());
131
132         System.out.println(map.containsValue("Dude"));
133         System.out.println(map.get("Hello"));
134
135         map.remove("Hello");
136
137         System.out.println(map);
138         System.out.println(map.size());
139
140         System.out.println(map.containsValue("Dude"));
141         System.out.println(map.get("Hello"));
142     }
143     */

144
145     /**
146      * Converts a string to a collection without calling size(). Iterators from
147      * this map may return less entries than the amount reported by size().
148      */

149     static String JavaDoc toString(Collection c) {
150         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
151         Iterator it = c.iterator();
152         buf.append("[");
153         for (int i = 0; it.hasNext(); i++) {
154             if (i > 0) {
155                 buf.append(", ");
156             }
157             buf.append(String.valueOf(it.next()));
158         }
159         buf.append("]");
160         return buf.toString();
161     }
162
163     /**
164      * The hash table data.
165      */

166     private transient Entry mTable[];
167
168     /**
169      * The total number of mappings in the hash table.
170      */

171     private transient int mCount;
172
173     /**
174      * The table is rehashed when its size exceeds this threshold. (The
175      * value of this field is (int)(capacity * loadFactor).)
176      *
177      * @serial
178      */

179     private int mThreshold;
180
181     /**
182      * The load factor for the hashtable.
183      *
184      * @serial
185      */

186     private float mLoadFactor;
187
188     /**
189      * The number of times this HashMap has been structurally modified
190      * Structural modifications are those that change the number of mappings in
191      * the HashMap or otherwise modify its internal structure (e.g.,
192      * rehash). This field is used to make iterators on Collection-views of
193      * the HashMap fail-fast. (See ConcurrentModificationException).
194      */

195     private transient int mModCount = 0;
196
197     // Views
198

199     private transient Set mKeySet = null;
200     private transient Set mEntrySet = null;
201     private transient Collection mValues = null;
202
203     /**
204      * Constructs a new, empty map with the specified initial
205      * capacity and the specified load factor.
206      *
207      * @param initialCapacity the initial capacity of the HashMap.
208      * @param loadFactor the load factor of the HashMap
209      * @throws IllegalArgumentException if the initial capacity is less
210      * than zero, or if the load factor is nonpositive.
211      */

212     public IdentityMap(int initialCapacity, float loadFactor) {
213         if (initialCapacity < 0) {
214             throw new IllegalArgumentException JavaDoc("Illegal Initial Capacity: "+
215                                                initialCapacity);
216         }
217
218         if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
219             throw new IllegalArgumentException JavaDoc("Illegal Load factor: "+
220                                                loadFactor);
221         }
222
223         if (initialCapacity == 0) {
224             initialCapacity = 1;
225         }
226
227         mLoadFactor = loadFactor;
228         mTable = new Entry[initialCapacity];
229         mThreshold = (int)(initialCapacity * loadFactor);
230     }
231     
232     /**
233      * Constructs a new, empty map with the specified initial capacity
234      * and default load factor, which is <tt>0.75</tt>.
235      *
236      * @param initialCapacity the initial capacity of the HashMap.
237      * @throws IllegalArgumentException if the initial capacity is less
238      * than zero.
239      */

240     public IdentityMap(int initialCapacity) {
241         this(initialCapacity, 0.75f);
242     }
243
244     /**
245      * Constructs a new, empty map with a default capacity and load
246      * factor, which is <tt>0.75</tt>.
247      */

248     public IdentityMap() {
249         this(11, 0.75f);
250     }
251
252     /**
253      * Constructs a new map with the same mappings as the given map. The
254      * map is created with a capacity of twice the number of mappings in
255      * the given map or 11 (whichever is greater), and a default load factor,
256      * which is <tt>0.75</tt>.
257      */

258     public IdentityMap(Map t) {
259         this(Math.max(2 * t.size(), 11), 0.75f);
260         putAll(t);
261     }
262
263     /**
264      * Returns the number of key-value mappings in this map, but this value
265      * may be larger than actual amount of entries produced by an iterator.
266      *
267      * @return the number of key-value mappings in this map.
268      */

269     public int size() {
270         return mCount;
271     }
272
273     /**
274      * Returns <tt>true</tt> if this map contains no key-value mappings.
275      *
276      * @return <tt>true</tt> if this map contains no key-value mappings.
277      */

278     public boolean isEmpty() {
279         return mCount == 0;
280     }
281
282     /**
283      * Returns <tt>true</tt> if this map maps one or more keys to the
284      * specified value.
285      *
286      * @param value value whose presence in this map is to be tested.
287      * @return <tt>true</tt> if this map maps one or more keys to the
288      * specified value.
289      */

290     public boolean containsValue(Object JavaDoc value) {
291         Entry tab[] = mTable;
292         
293         if (value == null) {
294             for (int i = tab.length ; i-- > 0 ;) {
295                 for (Entry e = tab[i], prev = null; e != null; e = e.mNext) {
296                     if (e.getKey() == null) {
297                         // Clean up after a cleared Reference.
298
mModCount++;
299                         if (prev != null) {
300                             prev.mNext = e.mNext;
301                         }
302                         else {
303                             tab[i] = e.mNext;
304                         }
305                         mCount--;
306                     }
307                     else if (e.mValue == null) {
308                         return true;
309                     }
310                     else {
311                         prev = e;
312                     }
313                 }
314             }
315         }
316         else {
317             for (int i = tab.length ; i-- > 0 ;) {
318                 for (Entry e = tab[i], prev = null; e != null; e = e.mNext) {
319                     if (e.getKey() == null) {
320                         // Clean up after a cleared Reference.
321
mModCount++;
322                         if (prev != null) {
323                             prev.mNext = e.mNext;
324                         }
325                         else {
326                             tab[i] = e.mNext;
327                         }
328                         mCount--;
329                     }
330                     else if (value.equals(e.mValue)) {
331                         return true;
332                     }
333                     else {
334                         prev = e;
335                     }
336                 }
337             }
338         }
339
340         return false;
341     }
342
343     /**
344      * Returns <tt>true</tt> if this map contains a mapping for the specified
345      * key.
346      *
347      * @return <tt>true</tt> if this map contains a mapping for the specified
348      * key.
349      * @param key key whose presence in this Map is to be tested.
350      */

351     public boolean containsKey(Object JavaDoc key) {
352         if (key == null) {
353             return false;
354         }
355
356         Entry tab[] = mTable;
357         int hash = System.identityHashCode(key);
358         int index = (hash & 0x7FFFFFFF) % tab.length;
359
360         for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
361             Object JavaDoc entryKey = e.getKey();
362
363             if (entryKey == null) {
364                 // Clean up after a cleared Reference.
365
mModCount++;
366                 if (prev != null) {
367                     prev.mNext = e.mNext;
368                 }
369                 else {
370                     tab[index] = e.mNext;
371                 }
372                 mCount--;
373             }
374             else if (e.mHash == hash && key == entryKey) {
375                 return true;
376             }
377             else {
378                 prev = e;
379             }
380         }
381
382         return false;
383     }
384
385     /**
386      * Returns the value to which this map maps the specified key. Returns
387      * <tt>null</tt> if the map contains no mapping for this key. A return
388      * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
389      * map contains no mapping for the key; it's also possible that the map
390      * explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt>
391      * operation may be used to distinguish these two cases.
392      *
393      * @return the value to which this map maps the specified key.
394      * @param key key whose associated value is to be returned.
395      */

396     public Object JavaDoc get(Object JavaDoc key) {
397         if (key == null) {
398             return null;
399         }
400
401         Entry tab[] = mTable;
402         int hash = System.identityHashCode(key);
403         int index = (hash & 0x7FFFFFFF) % tab.length;
404
405         for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
406             Object JavaDoc entryKey = e.getKey();
407
408             if (entryKey == null) {
409                 // Clean up after a cleared Reference.
410
mModCount++;
411                 if (prev != null) {
412                     prev.mNext = e.mNext;
413                 }
414                 else {
415                     tab[index] = e.mNext;
416                 }
417                 mCount--;
418             }
419             else if (e.mHash == hash && key == entryKey) {
420                 return e.mValue;
421             }
422             else {
423                 prev = e;
424             }
425         }
426
427         return null;
428     }
429
430     /**
431      * Scans the contents of this map, removing all entries that have a
432      * cleared weak key.
433      */

434     private void cleanup() {
435         Entry tab[] = mTable;
436         
437         for (int i = tab.length ; i-- > 0 ;) {
438             for (Entry e = tab[i], prev = null; e != null; e = e.mNext) {
439                 if (e.getKey() == null) {
440                     // Clean up after a cleared Reference.
441
mModCount++;
442                     if (prev != null) {
443                         prev.mNext = e.mNext;
444                     }
445                     else {
446                         tab[i] = e.mNext;
447                     }
448                     mCount--;
449                 }
450                 else {
451                     prev = e;
452                 }
453             }
454         }
455     }
456
457     /**
458      * Rehashes the contents of this map into a new <tt>HashMap</tt> instance
459      * with a larger capacity. This method is called automatically when the
460      * number of keys in this map exceeds its capacity and load factor.
461      */

462     private void rehash() {
463         int oldCapacity = mTable.length;
464         Entry oldMap[] = mTable;
465         
466         int newCapacity = oldCapacity * 2 + 1;
467         Entry newMap[] = new Entry[newCapacity];
468         
469         mModCount++;
470         mThreshold = (int)(newCapacity * mLoadFactor);
471         mTable = newMap;
472         
473         for (int i = oldCapacity ; i-- > 0 ;) {
474             for (Entry old = oldMap[i] ; old != null ; ) {
475                 Entry e = old;
476                 old = old.mNext;
477
478                 // Only copy entry if its key hasn't been cleared.
479
if (e.getKey() == null) {
480                     mCount--;
481                 }
482                 else {
483                     int index = (e.mHash & 0x7FFFFFFF) % newCapacity;
484                     e.mNext = newMap[index];
485                     newMap[index] = e;
486                 }
487             }
488         }
489     }
490     
491     /**
492      * Associates the specified value with the specified key in this map.
493      * If the map previously contained a mapping for this key, the old
494      * value is replaced.
495      *
496      * @param key key with which the specified value is to be associated.
497      * @param value value to be associated with the specified key.
498      * @return previous value associated with specified key, or <tt>null</tt>
499      * if there was no mapping for key. A <tt>null</tt> return can
500      * also indicate that the HashMap previously associated
501      * <tt>null</tt> with the specified key.
502      */

503     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
504         if (key == null) {
505             throw new NullPointerException JavaDoc("Null key is not permitted");
506         }
507
508         // Makes sure the key is not already in the HashMap.
509
Entry tab[] = mTable;
510         int hash = System.identityHashCode(key);
511         int index = (hash & 0x7FFFFFFF) % tab.length;
512
513         for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
514             Object JavaDoc entryKey = e.getKey();
515
516             if (entryKey == null) {
517                 // Clean up after a cleared Reference.
518
mModCount++;
519                 if (prev != null) {
520                     prev.mNext = e.mNext;
521                 }
522                 else {
523                     tab[index] = e.mNext;
524                 }
525                 mCount--;
526             }
527             else if (e.mHash == hash && key == entryKey) {
528                 Object JavaDoc old = e.mValue;
529                 e.mValue = value;
530                 return old;
531             }
532             else {
533                 prev = e;
534             }
535         }
536
537         mModCount++;
538
539         if (mCount >= mThreshold) {
540             // Cleanup the table if the threshold is exceeded.
541
cleanup();
542         }
543
544         if (mCount >= mThreshold) {
545             // Rehash the table if the threshold is still exceeded.
546
rehash();
547             tab = mTable;
548             index = (hash & 0x7FFFFFFF) % tab.length;
549         }
550         
551         // Creates the new entry.
552
Entry e = new Entry(hash, key, value, tab[index]);
553         tab[index] = e;
554         mCount++;
555         return null;
556     }
557     
558     /**
559      * Removes the mapping for this key from this map if present.
560      *
561      * @param key key whose mapping is to be removed from the map.
562      * @return previous value associated with specified key, or <tt>null</tt>
563      * if there was no mapping for key. A <tt>null</tt> return can
564      * also indicate that the map previously associated <tt>null</tt>
565      * with the specified key.
566      */

567     public Object JavaDoc remove(Object JavaDoc key) {
568         Entry tab[] = mTable;
569         int hash = System.identityHashCode(key);
570         int index = (hash & 0x7FFFFFFF) % tab.length;
571             
572         for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
573             Object JavaDoc entryKey = e.getKey();
574
575             if (entryKey == null) {
576                 // Clean up after a cleared Reference.
577
mModCount++;
578                 if (prev != null) {
579                     prev.mNext = e.mNext;
580                 }
581                 else {
582                     tab[index] = e.mNext;
583                 }
584                 mCount--;
585             }
586             else if (e.mHash == hash && key == entryKey) {
587                 mModCount++;
588                 if (prev != null) {
589                     prev.mNext = e.mNext;
590                 }
591                 else {
592                     tab[index] = e.mNext;
593                 }
594                 mCount--;
595
596                 Object JavaDoc oldValue = e.mValue;
597                 e.mValue = null;
598                 return oldValue;
599             }
600             else {
601                 prev = e;
602             }
603         }
604
605         return null;
606     }
607     
608     /**
609      * Copies all of the mappings from the specified map to this one.
610      *
611      * These mappings replace any mappings that this map had for any of the
612      * keys currently in the specified Map.
613      *
614      * @param t Mappings to be stored in this map.
615      */

616     public void putAll(Map t) {
617         Iterator i = t.entrySet().iterator();
618         while (i.hasNext()) {
619             Map.Entry e = (Map.Entry) i.next();
620             put(e.getKey(), e.getValue());
621         }
622     }
623
624     /**
625      * Removes all mappings from this map.
626      */

627     public void clear() {
628         Entry tab[] = mTable;
629         mModCount++;
630         for (int index = tab.length; --index >= 0; ) {
631             tab[index] = null;
632         }
633         mCount = 0;
634     }
635
636     /**
637      * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
638      * values themselves are not cloned.
639      *
640      * @return a shallow copy of this map.
641      */

642     public Object JavaDoc clone() {
643         try {
644             IdentityMap t = (IdentityMap)super.clone();
645             t.mTable = new Entry[mTable.length];
646             for (int i = mTable.length ; i-- > 0 ; ) {
647                 t.mTable[i] = (mTable[i] != null)
648                     ? (Entry)mTable[i].clone() : null;
649             }
650             t.mKeySet = null;
651             t.mEntrySet = null;
652             t.mValues = null;
653             t.mModCount = 0;
654             return t;
655         }
656         catch (CloneNotSupportedException JavaDoc e) {
657             // this shouldn't happen, since we are Cloneable
658
throw new InternalError JavaDoc();
659         }
660     }
661     
662     /**
663      * Returns a set view of the keys contained in this map. The set is
664      * backed by the map, so changes to the map are reflected in the set, and
665      * vice-versa. The set supports element removal, which removes the
666      * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
667      * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
668      * <tt>clear</tt> operations. It does not support the <tt>add</tt> or
669      * <tt>addAll</tt> operations.
670      *
671      * @return a set view of the keys contained in this map.
672      */

673     public Set keySet() {
674         if (mKeySet == null) {
675             mKeySet = new AbstractSet() {
676                 public Iterator iterator() {
677                     return getHashIterator(KEYS);
678                 }
679                 public int size() {
680                     return mCount;
681                 }
682                 public boolean contains(Object JavaDoc o) {
683                     return containsKey(o);
684                 }
685                 public boolean remove(Object JavaDoc o) {
686                     return o == null ? false : IdentityMap.this.remove(o) == o;
687                 }
688                 public void clear() {
689                     IdentityMap.this.clear();
690                 }
691                 public String JavaDoc toString() {
692                     return IdentityMap.this.toString(this);
693                 }
694             };
695         }
696         return mKeySet;
697     }
698     
699     /**
700      * Returns a collection view of the values contained in this map. The
701      * collection is backed by the map, so changes to the map are reflected in
702      * the collection, and vice-versa. The collection supports element
703      * removal, which removes the corresponding mapping from this map, via the
704      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
705      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
706      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
707      *
708      * @return a collection view of the values contained in this map.
709      */

710     public Collection values() {
711         if (mValues==null) {
712             mValues = new AbstractCollection() {
713                 public Iterator iterator() {
714                     return getHashIterator(VALUES);
715                 }
716                 public int size() {
717                     return mCount;
718                 }
719                 public boolean contains(Object JavaDoc o) {
720                     return containsValue(o);
721                 }
722                 public void clear() {
723                     IdentityMap.this.clear();
724                 }
725                 public String JavaDoc toString() {
726                     return IdentityMap.this.toString(this);
727                 }
728             };
729         }
730         return mValues;
731     }
732
733     /**
734      * Returns a collection view of the mappings contained in this map. Each
735      * element in the returned collection is a <tt>Map.Entry</tt>. The
736      * collection is backed by the map, so changes to the map are reflected in
737      * the collection, and vice-versa. The collection supports element
738      * removal, which removes the corresponding mapping from the map, via the
739      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
740      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
741      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
742      *
743      * @return a collection view of the mappings contained in this map.
744      * @see Map.Entry
745      */

746     public Set entrySet() {
747         if (mEntrySet==null) {
748             mEntrySet = new AbstractSet() {
749                 public Iterator iterator() {
750                     return getHashIterator(ENTRIES);
751                 }
752                 
753                 public boolean contains(Object JavaDoc o) {
754                     if (!(o instanceof Map.Entry)) {
755                         return false;
756                     }
757                     Map.Entry entry = (Map.Entry)o;
758                     Object JavaDoc key = entry.getKey();
759
760                     Entry tab[] = mTable;
761                     int hash = System.identityHashCode(key);
762                     int index = (hash & 0x7FFFFFFF) % tab.length;
763
764                     for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
765                         Object JavaDoc entryKey = e.getKey();
766                         
767                         if (entryKey == null) {
768                             // Clean up after a cleared Reference.
769
mModCount++;
770                             if (prev != null) {
771                                 prev.mNext = e.mNext;
772                             }
773                             else {
774                                 tab[index] = e.mNext;
775                             }
776                             mCount--;
777                         }
778                         else if (e.mHash == hash && e.identityEquals(entry)) {
779                             return true;
780                         }
781                         else {
782                             prev = e;
783                         }
784                     }
785
786                     return false;
787                 }
788
789                 public boolean remove(Object JavaDoc o) {
790                     if (!(o instanceof Map.Entry)) {
791                         return false;
792                     }
793                     Map.Entry entry = (Map.Entry)o;
794                     Object JavaDoc key = entry.getKey();
795                     Entry tab[] = mTable;
796                     int hash = System.identityHashCode(key);
797                     int index = (hash & 0x7FFFFFFF) % tab.length;
798
799                     for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
800                         Object JavaDoc entryKey = e.getKey();
801                         
802                         if (entryKey == null) {
803                             // Clean up after a cleared Reference.
804
mModCount++;
805                             if (prev != null) {
806                                 prev.mNext = e.mNext;
807                             }
808                             else {
809                                 tab[index] = e.mNext;
810                             }
811                             mCount--;
812                         }
813                         else if (e.mHash == hash && e.identityEquals(entry)) {
814                             mModCount++;
815                             if (prev != null) {
816                                 prev.mNext = e.mNext;
817                             }
818                             else {
819                                 tab[index] = e.mNext;
820                             }
821                             mCount--;
822
823                             e.mValue = null;
824                             return true;
825                         }
826                         else {
827                             prev = e;
828                         }
829                     }
830                     return false;
831                 }
832
833                 public int size() {
834                     return mCount;
835                 }
836                 
837                 public void clear() {
838                     IdentityMap.this.clear();
839                 }
840
841                 public String JavaDoc toString() {
842                     return IdentityMap.this.toString(this);
843                 }
844             };
845         }
846         
847         return mEntrySet;
848     }
849     
850     public String JavaDoc toString() {
851         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
852         Iterator it = entrySet().iterator();
853         
854         buf.append("{");
855         for (int i = 0; it.hasNext(); i++) {
856             if (i > 0) {
857                 buf.append(", ");
858             }
859             Map.Entry e = (Map.Entry)it.next();
860             buf.append(e.getKey() + "=" + e.getValue());
861         }
862         buf.append("}");
863         return buf.toString();
864     }
865
866     private Iterator getHashIterator(int type) {
867         if (mCount == 0) {
868             return cEmptyHashIterator;
869         }
870         else {
871             return new HashIterator(type);
872         }
873     }
874
875     /**
876      * HashMap collision list entry.
877      */

878     private static class Entry extends WeakReference implements Map.Entry {
879         int mHash;
880         Object JavaDoc mValue;
881         Entry mNext;
882         
883         Entry(int hash, Object JavaDoc key, Object JavaDoc value, Entry next) {
884             super(key);
885             mHash = hash;
886             mValue = value;
887             mNext = next;
888         }
889         
890         protected Object JavaDoc clone() {
891             return new Entry(mHash, getKey(), mValue,
892                              (mNext == null ? null : (Entry)mNext.clone()));
893         }
894         
895         // Map.Entry Ops
896

897         public Object JavaDoc getKey() {
898             return Entry.this.get();
899         }
900         
901         public Object JavaDoc getValue() {
902             return mValue;
903         }
904         
905         public Object JavaDoc setValue(Object JavaDoc value) {
906             Object JavaDoc oldValue = mValue;
907             mValue = value;
908             return oldValue;
909         }
910         
911         public boolean equals(Object JavaDoc o) {
912             if (!(o instanceof Map.Entry)) {
913                 return false;
914             }
915             Map.Entry e = (Map.Entry)o;
916
917             Object JavaDoc key = getKey();
918             
919             return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
920                 (mValue==null ? e.getValue()==null : mValue.equals(e.getValue()));
921         }
922
923         public boolean identityEquals(Map.Entry e) {
924             return (getKey() == e.getKey()) &&
925                 (mValue==null ? e.getValue()==null : mValue.equals(e.getValue()));
926         }
927         
928         public int hashCode() {
929             return mHash ^ (mValue==null ? 0 : mValue.hashCode());
930         }
931         
932         public String JavaDoc toString() {
933             return getKey() + "=" + mValue;
934         }
935     }
936
937     private class HashIterator implements Iterator {
938         private Entry[] mTable = IdentityMap.this.mTable;
939         private int mIndex = mTable.length;
940         private Entry mEntry;
941         // To ensure that the iterator doesn't return cleared entries, keep a
942
// hard reference to the key. Its existence will prevent the weak
943
// key from being cleared.
944
private Object JavaDoc mEntryKey;
945         private Entry mLastReturned;
946         private int mType;
947         
948         /**
949          * The modCount value that the iterator believes that the backing
950          * List should have. If this expectation is violated, the iterator
951          * has detected concurrent modification.
952          */

953         private int expectedModCount = mModCount;
954         
955         HashIterator(int type) {
956             mType = type;
957         }
958         
959         public boolean hasNext() {
960             while (mEntry == null ||
961                    (mEntryKey = mEntry.getKey()) == null) {
962                 if (mEntry != null) {
963                     // Clean up after a cleared Reference.
964
remove(mEntry);
965                     mEntry = mEntry.mNext;
966                 }
967                 else {
968                     if (mIndex <= 0) {
969                         return false;
970                     }
971                     else {
972                         mEntry = mTable[--mIndex];
973                     }
974                 }
975             }
976
977             return true;
978         }
979         
980         public Object JavaDoc next() {
981             if (mModCount != expectedModCount) {
982                 throw new ConcurrentModificationException();
983             }
984             
985             if (!hasNext()) {
986                 throw new NoSuchElementException();
987             }
988
989             mLastReturned = mEntry;
990             mEntry = mEntry.mNext;
991
992             return mType == KEYS ? mLastReturned.getKey() :
993                 (mType == VALUES ? mLastReturned.getValue() : mLastReturned);
994         }
995         
996         public void remove() {
997             if (mLastReturned == null) {
998                 throw new IllegalStateException JavaDoc();
999             }
1000            if (mModCount != expectedModCount) {
1001                throw new ConcurrentModificationException();
1002            }
1003            remove(mLastReturned);
1004            mLastReturned = null;
1005        }
1006
1007        private void remove(Entry toRemove) {
1008            Entry[] tab = mTable;
1009            int index = (toRemove.mHash & 0x7FFFFFFF) % tab.length;
1010            
1011            for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
1012                if (e == toRemove) {
1013                    mModCount++;
1014                    expectedModCount++;
1015                    if (prev == null) {
1016                        tab[index] = e.mNext;
1017                    }
1018                    else {
1019                        prev.mNext = e.mNext;
1020                    }
1021                    mCount--;
1022                    return;
1023                }
1024                else {
1025                    prev = e;
1026                }
1027            }
1028            throw new ConcurrentModificationException();
1029        }
1030    }
1031}
1032
Popular Tags