KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > ext > util > IdentityHashMap


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

52
53 package freemarker.ext.util;
54
55 import java.util.AbstractCollection JavaDoc;
56 import java.util.AbstractMap JavaDoc;
57 import java.util.AbstractSet JavaDoc;
58 import java.util.Collection JavaDoc;
59 import java.util.ConcurrentModificationException JavaDoc;
60 import java.util.Iterator JavaDoc;
61 import java.util.Map JavaDoc;
62 import java.util.NoSuchElementException JavaDoc;
63 import java.util.Set JavaDoc;
64
65 /**
66  * A variant of {@link java.util.HashMap} that uses
67  * {@link System#identityHashCode(Object)} for hashing, and reference comparison
68  * instead of {@link Object#equals(Object)}. Note that this applies only to keys,
69  * and not to values, i.e. {@link #containsValue(Object)} still uses {@link Object#equals(Object)}.
70  * @author Attila Szegedi
71  */

72 public class IdentityHashMap
73     extends AbstractMap JavaDoc
74     implements Map JavaDoc, Cloneable JavaDoc, java.io.Serializable JavaDoc
75 {
76
77     public static final long serialVersionUID = 362498820763181265L;
78     /**
79      * The hash table data.
80      */

81     private transient Entry table[];
82
83     /**
84      * The total number of mappings in the hash table.
85      */

86     private transient int count;
87
88     /**
89      * The table is rehashed when its size exceeds this threshold. (The
90      * value of this field is (int)(capacity * loadFactor).)
91      *
92      * @serial
93      */

94     private int threshold;
95
96     /**
97      * The load factor for the hashtable.
98      *
99      * @serial
100      */

101     private float loadFactor;
102
103     /**
104      * The number of times this IdentityHashMap has been structurally modified
105      * Structural modifications are those that change the number of mappings in
106      * the IdentityHashMap or otherwise modify its internal structure (e.g.,
107      * rehash). This field is used to make iterators on Collection-views of
108      * the IdentityHashMap fail-fast. (See ConcurrentModificationException).
109      */

110     private transient int modCount = 0;
111
112     /**
113      * Constructs a new, empty map with the specified initial
114      * capacity and the specified load factor.
115      *
116      * @param initialCapacity the initial capacity of the IdentityHashMap.
117      * @param loadFactor the load factor of the IdentityHashMap
118      * @throws IllegalArgumentException if the initial capacity is less
119      * than zero, or if the load factor is nonpositive.
120      */

121     public IdentityHashMap(int initialCapacity, float loadFactor)
122     {
123         if (initialCapacity < 0)
124             throw new IllegalArgumentException JavaDoc(
125                 "Illegal Initial Capacity: " + initialCapacity);
126         if (loadFactor <= 0 || Float.isNaN(loadFactor))
127             throw new IllegalArgumentException JavaDoc(
128                 "Illegal Load factor: " + loadFactor);
129         if (initialCapacity == 0)
130             initialCapacity = 1;
131         this.loadFactor = loadFactor;
132         table = new Entry[initialCapacity];
133         threshold = (int) (initialCapacity * loadFactor);
134     }
135
136     /**
137      * Constructs a new, empty map with the specified initial capacity
138      * and default load factor, which is <tt>0.75</tt>.
139      *
140      * @param initialCapacity the initial capacity of the IdentityHashMap.
141      * @throws IllegalArgumentException if the initial capacity is less
142      * than zero.
143      */

144     public IdentityHashMap(int initialCapacity)
145     {
146         this(initialCapacity, 0.75f);
147     }
148
149     /**
150      * Constructs a new, empty map with a default capacity and load
151      * factor, which is <tt>0.75</tt>.
152      */

153     public IdentityHashMap()
154     {
155         this(11, 0.75f);
156     }
157
158     /**
159      * Constructs a new map with the same mappings as the given map. The
160      * map is created with a capacity of twice the number of mappings in
161      * the given map or 11 (whichever is greater), and a default load factor,
162      * which is <tt>0.75</tt>.
163      *
164      * @param t the map whose mappings are to be placed in this map.
165      */

166     public IdentityHashMap(Map JavaDoc t)
167     {
168         this(Math.max(2 * t.size(), 11), 0.75f);
169         putAll(t);
170     }
171
172     /**
173      * Returns the number of key-value mappings in this map.
174      *
175      * @return the number of key-value mappings in this map.
176      */

177     public int size()
178     {
179         return count;
180     }
181
182     /**
183      * Returns <tt>true</tt> if this map contains no key-value mappings.
184      *
185      * @return <tt>true</tt> if this map contains no key-value mappings.
186      */

187     public boolean isEmpty()
188     {
189         return count == 0;
190     }
191
192     /**
193      * Returns <tt>true</tt> if this map maps one or more keys to the
194      * specified value.
195      *
196      * @param value value whose presence in this map is to be tested.
197      * @return <tt>true</tt> if this map maps one or more keys to the
198      * specified value.
199      */

200     public boolean containsValue(Object JavaDoc value)
201     {
202         Entry tab[] = table;
203
204         if (value == null)
205         {
206             for (int i = tab.length; i-- > 0;)
207                 for (Entry e = tab[i]; e != null; e = e.next)
208                     if (e.value == null)
209                         return true;
210         }
211         else
212         {
213             for (int i = tab.length; i-- > 0;)
214                 for (Entry e = tab[i]; e != null; e = e.next)
215                     if (value.equals(e.value))
216                         return true;
217         }
218
219         return false;
220     }
221
222     /**
223      * Returns <tt>true</tt> if this map contains a mapping for the specified
224      * key.
225      *
226      * @return <tt>true</tt> if this map contains a mapping for the specified
227      * key.
228      * @param key key whose presence in this Map is to be tested.
229      */

230     public boolean containsKey(Object JavaDoc key)
231     {
232         Entry tab[] = table;
233         if (key != null)
234         {
235             int hash = System.identityHashCode(key);
236             int index = (hash & 0x7FFFFFFF) % tab.length;
237             for (Entry e = tab[index]; e != null; e = e.next)
238                 if (e.hash == hash && key == e.key)
239                     return true;
240         }
241         else
242         {
243             for (Entry e = tab[0]; e != null; e = e.next)
244                 if (e.key == null)
245                     return true;
246         }
247
248         return false;
249     }
250
251     /**
252      * Returns the value to which this map maps the specified key. Returns
253      * <tt>null</tt> if the map contains no mapping for this key. A return
254      * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
255      * map contains no mapping for the key; it's also possible that the map
256      * explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt>
257      * operation may be used to distinguish these two cases.
258      *
259      * @return the value to which this map maps the specified key.
260      * @param key key whose associated value is to be returned.
261      */

262     public Object JavaDoc get(Object JavaDoc key)
263     {
264         Entry tab[] = table;
265
266         if (key != null)
267         {
268             int hash = System.identityHashCode(key);
269             int index = (hash & 0x7FFFFFFF) % tab.length;
270             for (Entry e = tab[index]; e != null; e = e.next)
271                 if ((e.hash == hash) && key == e.key)
272                     return e.value;
273         }
274         else
275         {
276             for (Entry e = tab[0]; e != null; e = e.next)
277                 if (e.key == null)
278                     return e.value;
279         }
280
281         return null;
282     }
283
284     /**
285      * Rehashes the contents of this map into a new <tt>IdentityHashMap</tt> instance
286      * with a larger capacity. This method is called automatically when the
287      * number of keys in this map exceeds its capacity and load factor.
288      */

289     private void rehash()
290     {
291         int oldCapacity = table.length;
292         Entry oldMap[] = table;
293
294         int newCapacity = oldCapacity * 2 + 1;
295         Entry newMap[] = new Entry[newCapacity];
296
297         modCount++;
298         threshold = (int) (newCapacity * loadFactor);
299         table = newMap;
300
301         for (int i = oldCapacity; i-- > 0;)
302         {
303             for (Entry old = oldMap[i]; old != null;)
304             {
305                 Entry e = old;
306                 old = old.next;
307
308                 int index = (e.hash & 0x7FFFFFFF) % newCapacity;
309                 e.next = newMap[index];
310                 newMap[index] = e;
311             }
312         }
313     }
314
315     /**
316      * Associates the specified value with the specified key in this map.
317      * If the map previously contained a mapping for this key, the old
318      * value is replaced.
319      *
320      * @param key key with which the specified value is to be associated.
321      * @param value value to be associated with the specified key.
322      * @return previous value associated with specified key, or <tt>null</tt>
323      * if there was no mapping for key. A <tt>null</tt> return can
324      * also indicate that the IdentityHashMap previously associated
325      * <tt>null</tt> with the specified key.
326      */

327     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value)
328     {
329         // Makes sure the key is not already in the IdentityHashMap.
330
Entry tab[] = table;
331         int hash = 0;
332         int index = 0;
333
334         if (key != null)
335         {
336             hash = System.identityHashCode(key);
337             index = (hash & 0x7FFFFFFF) % tab.length;
338             for (Entry e = tab[index]; e != null; e = e.next)
339             {
340                 if ((e.hash == hash) && key == e.key)
341                 {
342                     Object JavaDoc old = e.value;
343                     e.value = value;
344                     return old;
345                 }
346             }
347         }
348         else
349         {
350             for (Entry e = tab[0]; e != null; e = e.next)
351             {
352                 if (e.key == null)
353                 {
354                     Object JavaDoc old = e.value;
355                     e.value = value;
356                     return old;
357                 }
358             }
359         }
360
361         modCount++;
362         if (count >= threshold)
363         {
364             // Rehash the table if the threshold is exceeded
365
rehash();
366
367             tab = table;
368             index = (hash & 0x7FFFFFFF) % tab.length;
369         }
370
371         // Creates the new entry.
372
Entry e = new Entry(hash, key, value, tab[index]);
373         tab[index] = e;
374         count++;
375         return null;
376     }
377
378     /**
379      * Removes the mapping for this key from this map if present.
380      *
381      * @param key key whose mapping is to be removed from the map.
382      * @return previous value associated with specified key, or <tt>null</tt>
383      * if there was no mapping for key. A <tt>null</tt> return can
384      * also indicate that the map previously associated <tt>null</tt>
385      * with the specified key.
386      */

387     public Object JavaDoc remove(Object JavaDoc key)
388     {
389         Entry tab[] = table;
390
391         if (key != null)
392         {
393             int hash = System.identityHashCode(key);
394             int index = (hash & 0x7FFFFFFF) % tab.length;
395
396             for (Entry e = tab[index], prev = null;
397                 e != null;
398                 prev = e, e = e.next)
399             {
400                 if ((e.hash == hash) && key == e.key)
401                 {
402                     modCount++;
403                     if (prev != null)
404                         prev.next = e.next;
405                     else
406                         tab[index] = e.next;
407
408                     count--;
409                     Object JavaDoc oldValue = e.value;
410                     e.value = null;
411                     return oldValue;
412                 }
413             }
414         }
415         else
416         {
417             for (Entry e = tab[0], prev = null;
418                 e != null;
419                 prev = e, e = e.next)
420             {
421                 if (e.key == null)
422                 {
423                     modCount++;
424                     if (prev != null)
425                         prev.next = e.next;
426                     else
427                         tab[0] = e.next;
428
429                     count--;
430                     Object JavaDoc oldValue = e.value;
431                     e.value = null;
432                     return oldValue;
433                 }
434             }
435         }
436
437         return null;
438     }
439
440     /**
441      * Copies all of the mappings from the specified map to this one.
442      *
443      * These mappings replace any mappings that this map had for any of the
444      * keys currently in the specified Map.
445      *
446      * @param t Mappings to be stored in this map.
447      */

448     public void putAll(Map JavaDoc t)
449     {
450         Iterator JavaDoc i = t.entrySet().iterator();
451         while (i.hasNext())
452         {
453             Map.Entry JavaDoc e = (Map.Entry JavaDoc) i.next();
454             put(e.getKey(), e.getValue());
455         }
456     }
457
458     /**
459      * Removes all mappings from this map.
460      */

461     public void clear()
462     {
463         Entry tab[] = table;
464         modCount++;
465         for (int index = tab.length; --index >= 0;)
466             tab[index] = null;
467         count = 0;
468     }
469
470     /**
471      * Returns a shallow copy of this <tt>IdentityHashMap</tt> instance: the keys and
472      * values themselves are not cloned.
473      *
474      * @return a shallow copy of this map.
475      */

476     public Object JavaDoc clone()
477     {
478         try
479         {
480             IdentityHashMap t = (IdentityHashMap) super.clone();
481             t.table = new Entry[table.length];
482             for (int i = table.length; i-- > 0;)
483             {
484                 t.table[i] =
485                     (table[i] != null) ? (Entry) table[i].clone() : null;
486             }
487             t.keySet = null;
488             t.entrySet = null;
489             t.values = null;
490             t.modCount = 0;
491             return t;
492         }
493         catch (CloneNotSupportedException JavaDoc e)
494         {
495             // this shouldn't happen, since we are Cloneable
496
throw new InternalError JavaDoc();
497         }
498     }
499
500     // Views
501

502     private transient Set JavaDoc keySet = null;
503     private transient Set JavaDoc entrySet = null;
504     private transient Collection JavaDoc values = null;
505
506     /**
507      * Returns a set view of the keys contained in this map. The set is
508      * backed by the map, so changes to the map are reflected in the set, and
509      * vice versa. The set supports element removal, which removes the
510      * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
511      * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
512      * <tt>clear</tt> operations. It does not support the <tt>add</tt> or
513      * <tt>addAll</tt> operations.
514      *
515      * @return a set view of the keys contained in this map.
516      */

517     public Set JavaDoc keySet()
518     {
519         if (keySet == null)
520         {
521             keySet = new AbstractSet JavaDoc()
522             {
523                 public Iterator JavaDoc iterator()
524                 {
525                     return getHashIterator(KEYS);
526                 }
527                 public int size()
528                 {
529                     return count;
530                 }
531                 public boolean contains(Object JavaDoc o)
532                 {
533                     return containsKey(o);
534                 }
535                 public boolean remove(Object JavaDoc o)
536                 {
537                     int oldSize = count;
538                     IdentityHashMap.this.remove(o);
539                     return count != oldSize;
540                 }
541                 public void clear()
542                 {
543                     IdentityHashMap.this.clear();
544                 }
545             };
546         }
547         return keySet;
548     }
549
550     /**
551      * Returns a collection view of the values contained in this map. The
552      * collection is backed by the map, so changes to the map are reflected in
553      * the collection, and vice versa. The collection supports element
554      * removal, which removes the corresponding mapping from this map, via the
555      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
556      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
557      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
558      *
559      * @return a collection view of the values contained in this map.
560      */

561     public Collection JavaDoc values()
562     {
563         if (values == null)
564         {
565             values = new AbstractCollection JavaDoc()
566             {
567                 public Iterator JavaDoc iterator()
568                 {
569                     return getHashIterator(VALUES);
570                 }
571                 public int size()
572                 {
573                     return count;
574                 }
575                 public boolean contains(Object JavaDoc o)
576                 {
577                     return containsValue(o);
578                 }
579                 public void clear()
580                 {
581                     IdentityHashMap.this.clear();
582                 }
583             };
584         }
585         return values;
586     }
587
588     /**
589      * Returns a collection view of the mappings contained in this map. Each
590      * element in the returned collection is a <tt>Map.Entry</tt>. The
591      * collection is backed by the map, so changes to the map are reflected in
592      * the collection, and vice versa. The collection supports element
593      * removal, which removes the corresponding mapping from the map, via the
594      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
595      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
596      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
597      *
598      * @return a collection view of the mappings contained in this map.
599      * @see java.util.Map.Entry
600      */

601     public Set JavaDoc entrySet()
602     {
603         if (entrySet == null)
604         {
605             entrySet = new AbstractSet JavaDoc()
606             {
607                 public Iterator JavaDoc iterator()
608                 {
609                     return getHashIterator(ENTRIES);
610                 }
611
612                 public boolean contains(Object JavaDoc o)
613                 {
614                     if (!(o instanceof Map.Entry JavaDoc))
615                         return false;
616                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) o;
617                     Object JavaDoc key = entry.getKey();
618                     Entry tab[] = table;
619                     int hash = (key == null ? 0 : System.identityHashCode(key));
620                     int index = (hash & 0x7FFFFFFF) % tab.length;
621
622                     for (Entry e = tab[index]; e != null; e = e.next)
623                         if (e.hash == hash && e.equals(entry))
624                             return true;
625                     return false;
626                 }
627
628                 public boolean remove(Object JavaDoc o)
629                 {
630                     if (!(o instanceof Map.Entry JavaDoc))
631                         return false;
632                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) o;
633                     Object JavaDoc key = entry.getKey();
634                     Entry tab[] = table;
635                     int hash = (key == null ? 0 : System.identityHashCode(key));
636                     int index = (hash & 0x7FFFFFFF) % tab.length;
637
638                     for (Entry e = tab[index], prev = null;
639                         e != null;
640                         prev = e, e = e.next)
641                     {
642                         if (e.hash == hash && e.equals(entry))
643                         {
644                             modCount++;
645                             if (prev != null)
646                                 prev.next = e.next;
647                             else
648                                 tab[index] = e.next;
649
650                             count--;
651                             e.value = null;
652                             return true;
653                         }
654                     }
655                     return false;
656                 }
657
658                 public int size()
659                 {
660                     return count;
661                 }
662
663                 public void clear()
664                 {
665                     IdentityHashMap.this.clear();
666                 }
667             };
668         }
669
670         return entrySet;
671     }
672
673     private Iterator JavaDoc getHashIterator(int type)
674     {
675         if (count == 0)
676         {
677             return emptyHashIterator;
678         }
679         else
680         {
681             return new HashIterator(type);
682         }
683     }
684
685     /**
686      * IdentityHashMap collision list entry.
687      */

688     private static class Entry implements Map.Entry JavaDoc
689     {
690         int hash;
691         Object JavaDoc key;
692         Object JavaDoc value;
693         Entry next;
694
695         Entry(int hash, Object JavaDoc key, Object JavaDoc value, Entry next)
696         {
697             this.hash = hash;
698             this.key = key;
699             this.value = value;
700             this.next = next;
701         }
702
703         protected Object JavaDoc clone()
704         {
705             return new Entry(
706                 hash,
707                 key,
708                 value,
709                 (next == null ? null : (Entry) next.clone()));
710         }
711
712         // Map.Entry Ops
713

714         public Object JavaDoc getKey()
715         {
716             return key;
717         }
718
719         public Object JavaDoc getValue()
720         {
721             return value;
722         }
723
724         public Object JavaDoc setValue(Object JavaDoc value)
725         {
726             Object JavaDoc oldValue = this.value;
727             this.value = value;
728             return oldValue;
729         }
730
731         public boolean equals(Object JavaDoc o)
732         {
733             if (!(o instanceof Map.Entry JavaDoc))
734                 return false;
735             Map.Entry JavaDoc e = (Map.Entry JavaDoc) o;
736
737             return (key == e.getKey())
738                 && (value == null
739                     ? e.getValue() == null
740                     : value.equals(e.getValue()));
741         }
742
743         public int hashCode()
744         {
745             return hash ^ (value == null ? 0 : value.hashCode());
746         }
747
748         public String JavaDoc toString()
749         {
750             return key + "=" + value;
751         }
752     }
753
754     // Types of Iterators
755
private static final int KEYS = 0;
756     private static final int VALUES = 1;
757     private static final int ENTRIES = 2;
758
759     private static EmptyHashIterator emptyHashIterator =
760         new EmptyHashIterator();
761
762     private static class EmptyHashIterator implements Iterator JavaDoc
763     {
764
765         EmptyHashIterator()
766         {
767
768         }
769
770         public boolean hasNext()
771         {
772             return false;
773         }
774
775         public Object JavaDoc next()
776         {
777             throw new NoSuchElementException JavaDoc();
778         }
779
780         public void remove()
781         {
782             throw new IllegalStateException JavaDoc();
783         }
784
785     }
786
787     private class HashIterator implements Iterator JavaDoc
788     {
789         Entry[] table = IdentityHashMap.this.table;
790         int index = table.length;
791         Entry entry = null;
792         Entry lastReturned = null;
793         int type;
794
795         /**
796          * The modCount value that the iterator believes that the backing
797          * List should have. If this expectation is violated, the iterator
798          * has detected concurrent modification.
799          */

800         private int expectedModCount = modCount;
801
802         HashIterator(int type)
803         {
804             this.type = type;
805         }
806
807         public boolean hasNext()
808         {
809             Entry e = entry;
810             int i = index;
811             Entry t[] = table;
812             /* Use locals for faster loop iteration */
813             while (e == null && i > 0)
814                 e = t[--i];
815             entry = e;
816             index = i;
817             return e != null;
818         }
819
820         public Object JavaDoc next()
821         {
822             if (modCount != expectedModCount)
823                 throw new ConcurrentModificationException JavaDoc();
824
825             Entry et = entry;
826             int i = index;
827             Entry t[] = table;
828
829             /* Use locals for faster loop iteration */
830             while (et == null && i > 0)
831                 et = t[--i];
832
833             entry = et;
834             index = i;
835             if (et != null)
836             {
837                 Entry e = lastReturned = entry;
838                 entry = e.next;
839                 return type == KEYS ? e.key : (type == VALUES ? e.value : e);
840             }
841             throw new NoSuchElementException JavaDoc();
842         }
843
844         public void remove()
845         {
846             if (lastReturned == null)
847                 throw new IllegalStateException JavaDoc();
848             if (modCount != expectedModCount)
849                 throw new ConcurrentModificationException JavaDoc();
850
851             Entry[] tab = IdentityHashMap.this.table;
852             int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
853
854             for (Entry e = tab[index], prev = null;
855                 e != null;
856                 prev = e, e = e.next)
857             {
858                 if (e == lastReturned)
859                 {
860                     modCount++;
861                     expectedModCount++;
862                     if (prev == null)
863                         tab[index] = e.next;
864                     else
865                         prev.next = e.next;
866                     count--;
867                     lastReturned = null;
868                     return;
869                 }
870             }
871             throw new ConcurrentModificationException JavaDoc();
872         }
873     }
874
875     /**
876      * Save the state of the <tt>IdentityHashMap</tt> instance to a stream (i.e.,
877      * serialize it).
878      *
879      * @serialData The <i>capacity</i> of the IdentityHashMap (the length of the
880      * bucket array) is emitted (int), followed by the
881      * <i>size</i> of the IdentityHashMap (the number of key-value
882      * mappings), followed by the key (Object) and value (Object)
883      * for each key-value mapping represented by the IdentityHashMap
884      * The key-value mappings are emitted in no particular order.
885      */

886     private void writeObject(java.io.ObjectOutputStream JavaDoc s)
887         throws java.io.IOException JavaDoc
888     {
889         // Write out the threshold, loadfactor, and any hidden stuff
890
s.defaultWriteObject();
891
892         // Write out number of buckets
893
s.writeInt(table.length);
894
895         // Write out size (number of Mappings)
896
s.writeInt(count);
897
898         // Write out keys and values (alternating)
899
for (int index = table.length - 1; index >= 0; index--)
900         {
901             Entry entry = table[index];
902
903             while (entry != null)
904             {
905                 s.writeObject(entry.key);
906                 s.writeObject(entry.value);
907                 entry = entry.next;
908             }
909         }
910     }
911
912     /**
913      * Reconstitute the <tt>IdentityHashMap</tt> instance from a stream (i.e.,
914      * deserialize it).
915      */

916     private void readObject(java.io.ObjectInputStream JavaDoc s)
917         throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc
918     {
919         // Read in the threshold, loadfactor, and any hidden stuff
920
s.defaultReadObject();
921
922         // Read in number of buckets and allocate the bucket array;
923
int numBuckets = s.readInt();
924         table = new Entry[numBuckets];
925
926         // Read in size (number of Mappings)
927
int size = s.readInt();
928
929         // Read the keys and values, and put the mappings in the IdentityHashMap
930
for (int i = 0; i < size; i++)
931         {
932             Object JavaDoc key = s.readObject();
933             Object JavaDoc value = s.readObject();
934             put(key, value);
935         }
936     }
937
938     int capacity()
939     {
940         return table.length;
941     }
942
943     float loadFactor()
944     {
945         return loadFactor;
946     }
947 }
948
Popular Tags