View Javadoc

1   /*
2    * $Header: /home/projects/jaxen/scm/jaxen/src/java/main/org/jaxen/util/IdentityHashMap.java,v 1.8 2005/04/06 21:48:25 elharo Exp $
3    * $Revision: 1.8 $
4    * $Date: 2005/04/06 21:48:25 $
5    *
6    * ====================================================================
7    *
8    * Copyright (C) 2000-2002 bob mcwhirter & James Strachan.
9    * All rights reserved.
10   *
11   * Redistribution and use in source and binary forms, with or without
12   * modification, are permitted provided that the following conditions
13   * are met:
14   * 
15   * 1. Redistributions of source code must retain the above copyright
16   *    notice, this list of conditions, and the following disclaimer.
17   *
18   * 2. Redistributions in binary form must reproduce the above copyright
19   *    notice, this list of conditions, and the disclaimer that follows 
20   *    these conditions in the documentation and/or other materials 
21   *    provided with the distribution.
22   *
23   * 3. The name "Jaxen" must not be used to endorse or promote products
24   *    derived from this software without prior written permission.  For
25   *    written permission, please contact license@jaxen.org.
26   * 
27   * 4. Products derived from this software may not be called "Jaxen", nor
28   *    may "Jaxen" appear in their name, without prior written permission
29   *    from the Jaxen Project Management (pm@jaxen.org).
30   * 
31   * In addition, we request (but do not require) that you include in the 
32   * end-user documentation provided with the redistribution and/or in the 
33   * software itself an acknowledgement equivalent to the following:
34   *     "This product includes software developed by the
35   *      Jaxen Project (http://www.jaxen.org/)."
36   * Alternatively, the acknowledgment may be graphical using the logos 
37   * available at http://www.jaxen.org/
38   *
39   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42   * DISCLAIMED.  IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT
43   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50   * SUCH DAMAGE.
51   *
52   * ====================================================================
53   * This software consists of voluntary contributions made by many 
54   * individuals on behalf of the Jaxen Project and was originally 
55   * created by bob mcwhirter <bob@werken.com> and 
56   * James Strachan <jstrachan@apache.org>.  For more information on the 
57   * Jaxen Project, please see <http://www.jaxen.org/>.
58   * 
59   * $Id: IdentityHashMap.java,v 1.8 2005/04/06 21:48:25 elharo Exp $
60   */
61  
62  package org.jaxen.util;
63  import java.io.IOException;
64  import java.util.AbstractCollection;
65  import java.util.AbstractMap;
66  import java.util.AbstractSet;
67  import java.util.Collection;
68  import java.util.ConcurrentModificationException;
69  import java.util.Iterator;
70  import java.util.Map;
71  import java.util.NoSuchElementException;
72  import java.util.Set;
73  
74  public class IdentityHashMap extends AbstractMap implements Map, Cloneable,
75                                           java.io.Serializable {
76      /***
77       * The hash table data.
78       */
79      private transient Entry table[];
80  
81      /***
82       * The total number of mappings in the hash table.
83       */
84      private transient int count;
85  
86      /***
87       * The table is rehashed when its size exceeds this threshold.  (The
88       * value of this field is (int)(capacity * loadFactor).)
89       *
90       * @serial
91       */
92      private int threshold;
93  
94      /***
95       * The load factor for the hashtable.
96       *
97       * @serial
98       */
99      private float loadFactor;
100 
101     /***
102      * The number of times this HashMap has been structurally modified
103      * Structural modifications are those that change the number of mappings in
104      * the HashMap or otherwise modify its internal structure (e.g.,
105      * rehash).  This field is used to make iterators on Collection-views of
106      * the HashMap fail-fast.  (See ConcurrentModificationException).
107      */
108     private transient int modCount = 0;
109 
110     /***
111      * Constructs a new, empty map with the specified initial 
112      * capacity and the specified load factor. 
113      *
114      * @param      initialCapacity   the initial capacity of the HashMap
115      * @param      loadFactor        the load factor of the HashMap
116      * @throws     IllegalArgumentException  if the initial capacity is less
117      *               than zero, or if the load factor is non-positive
118      */
119     public IdentityHashMap(int initialCapacity, float loadFactor) {
120         if (initialCapacity < 0)
121             throw new IllegalArgumentException("Illegal Initial Capacity: "+
122                                                initialCapacity);
123         if (loadFactor <= 0 || Float.isNaN(loadFactor))
124             throw new IllegalArgumentException("Illegal Load factor: "+
125                                                loadFactor);
126         if (initialCapacity==0)
127             initialCapacity = 1;
128         this.loadFactor = loadFactor;
129         table = new Entry[initialCapacity];
130         threshold = (int)(initialCapacity * loadFactor);
131     }
132 
133     /***
134      * Constructs a new, empty map with the specified initial capacity
135      * and default load factor, which is <code>0.75</code>.
136      *
137      * @param   initialCapacity   the initial capacity of the HashMap
138      * @throws  IllegalArgumentException if the initial capacity is less
139      *              than zero
140      */
141     public IdentityHashMap(int initialCapacity) {
142         this(initialCapacity, 0.75f);
143     }
144 
145     /***
146      * Constructs a new, empty map with a default capacity and load
147      * factor, which is <code>0.75</code>.
148      */
149     public IdentityHashMap() {
150         this(11, 0.75f);
151     }
152 
153     /***
154      * Constructs a new map with the same mappings as the given map.  The
155      * map is created with a capacity of twice the number of mappings in
156      * the given map or 11 (whichever is greater), and a default load factor,
157      * which is <code>0.75</code>.
158      *
159      * @param t the map whose mappings are to be placed in this map
160      */
161     public IdentityHashMap(Map t) {
162         this(Math.max(2*t.size(), 11), 0.75f);
163         putAll(t);
164     }
165 
166     /***
167      * Returns the number of key-value mappings in this map
168      *
169      * @return the number of key-value mappings in this map
170      */
171     public int size() {
172         return count;
173     }
174 
175     /***
176      * Returns <code>true</code> if this map contains no key-value mappings
177      *
178      * @return <code>true</code> if this map contains no key-value mappings
179      */
180     public boolean isEmpty() {
181         return count == 0;
182     }
183 
184     /***
185      * Returns <code>true</code> if this map maps one or more keys to the
186      * specified value.
187      *
188      * @param value value whose presence in this map is to be tested.
189      * @return <code>true</code> if this map maps one or more keys to the
190      *         specified value
191      */
192     public boolean containsValue(Object value) {
193         Entry tab[] = table;
194 
195         if (value==null) {
196             for (int i = tab.length ; i > 0 ; i--)
197                 for (Entry e = tab[i] ; e != null ; e = e.next)
198                     if (e.value==null)
199                         return true;
200         } else {
201             for (int i = tab.length ; i > 0 ; i--)
202                 for (Entry e = tab[i] ; e != null ; e = e.next)
203                     if (value.equals(e.value))
204                         return true;
205         }
206 
207         return false;
208     }
209 
210     /***
211      * Returns <code>true</code> if this map contains a mapping for the specified
212      * key.
213      * 
214      * @return <code>true</code> if this map contains a mapping for the specified
215      * key
216      * @param key key whose presence in this Map is to be tested
217      */
218     public boolean containsKey(Object key) {
219         Entry tab[] = table;
220         if (key != null) {
221             //int hash = key.hashCode();
222             int hash = System.identityHashCode( key );
223             int index = (hash & 0x7FFFFFFF) % tab.length;
224             for (Entry e = tab[index]; e != null; e = e.next)
225                 // if (e.hash==hash && key.equals(e.key))
226                 if (e.hash==hash && ( key == e.key ) )
227                     return true;
228         } else {
229             for (Entry e = tab[0]; e != null; e = e.next)
230                 if (e.key==null) return true;
231         }
232 
233         return false;
234     }
235 
236     /***
237      * Returns the value to which this map maps the specified key.  Returns
238      * <code>null</code> if the map contains no mapping for this key.  A return
239      * value of <code>null</code> does not <i>necessarily</i> indicate that the
240      * map contains no mapping for the key; it's also possible that the map
241      * explicitly maps the key to <code>null</code>.  The <code>containsKey</code>
242      * operation may be used to distinguish these two cases.
243      *
244      * @return the value to which this map maps the specified key
245      * @param key key whose associated value is to be returned
246      */
247     public Object get(Object key) {
248         Entry tab[] = table;
249 
250         if (key != null) {
251             // int hash = key.hashCode();
252             int hash = System.identityHashCode( key );
253             int index = (hash & 0x7FFFFFFF) % tab.length;
254             for (Entry e = tab[index]; e != null; e = e.next)
255                 // if ((e.hash == hash) && key.equals(e.key))
256                 if ((e.hash == hash) && ( key == e.key ) )
257                     return e.value;
258         } else {
259             for (Entry e = tab[0]; e != null; e = e.next)
260                 if (e.key==null)
261                     return e.value;
262         }
263 
264         return null;
265     }
266 
267     /***
268      * Rehashes the contents of this map into a new <code>IdentityHashMap</code> instance
269      * with a larger capacity. This method is called automatically when the
270      * number of keys in this map exceeds its capacity and load factor.
271      */
272     private void rehash() {
273         int oldCapacity = table.length;
274         Entry oldMap[] = table;
275 
276         int newCapacity = oldCapacity * 2 + 1;
277         Entry newMap[] = new Entry[newCapacity];
278 
279         modCount++;
280         threshold = (int)(newCapacity * loadFactor);
281         table = newMap;
282 
283         for (int i = oldCapacity ; i-- > 0 ;) {
284             for (Entry old = oldMap[i] ; old != null ; ) {
285                 Entry e = old;
286                 old = old.next;
287 
288                 int index = (e.hash & 0x7FFFFFFF) % newCapacity;
289                 e.next = newMap[index];
290                 newMap[index] = e;
291             }
292         }
293     }
294 
295     /***
296      * Associates the specified value with the specified key in this map.
297      * If the map previously contained a mapping for this key, the old
298      * value is replaced.
299      *
300      * @param key key with which the specified value is to be associated
301      * @param value value to be associated with the specified key
302      * @return previous value associated with specified key, or <code>null</code>
303      *         if there was no mapping for key.  A <code>null</code> return can
304      *         also indicate that the IdentityHashMap previously associated
305      *         <code>null</code> with the specified key.
306      */
307     public Object put(Object key, Object value) {
308         // XXX this method is a HotSpot in Jaxen
309         // Makes sure the key is not already in the IdentityHashMap.
310         
311         // I think this copy, which has no semantic effect, is being 
312         // done to speed things up by using a local variable instead of a field
313         // access. I'm not sure this is significant. - ERH
314         Entry tab[] = table;
315         int hash = 0;
316         int index = 0;
317 
318         if (key != null) {
319             hash = System.identityHashCode( key );
320             index = (hash & 0x7FFFFFFF) % tab.length;
321             for (Entry e = tab[index] ; e != null ; e = e.next) {
322                 if ((e.hash == hash) && ( key == e.key ) ) {
323                     Object old = e.value;
324                     e.value = value;
325                     return old;
326                 }
327             }
328         } else {
329             for (Entry e = tab[0] ; e != null ; e = e.next) {
330                 if (e.key == null) {
331                     Object old = e.value;
332                     e.value = value;
333                     return old;
334                 }
335             }
336         }
337 
338         modCount++;
339         if (count >= threshold) {
340             // Rehash the table if the threshold is exceeded
341             rehash();
342 
343             tab = table;
344             index = (hash & 0x7FFFFFFF) % tab.length;
345         }
346 
347         // Creates the new entry.
348         Entry e = new Entry(hash, key, value, tab[index]);
349         tab[index] = e;
350         count++;
351         return null;
352     }
353 
354     /***
355      * Removes the mapping for this key from this map if present.
356      *
357      * @param key key whose mapping is to be removed from the map
358      * @return previous value associated with specified key, or <code>null</code>
359      *         if there was no mapping for key.  A <code>null</code> return can
360      *         also indicate that the map previously associated <code>null</code>
361      *         with the specified key.
362      */
363     public Object remove(Object key) {
364         Entry tab[] = table;
365 
366         if (key != null) {
367             // int hash = key.hashCode();
368             int hash = System.identityHashCode( key );
369             int index = (hash & 0x7FFFFFFF) % tab.length;
370 
371             for (Entry e = tab[index], prev = null; e != null;
372                  prev = e, e = e.next) {
373                 // if ((e.hash == hash) && key.equals(e.key)) {
374                 if ((e.hash == hash) && ( key == e.key ) ) {
375                     modCount++;
376                     if (prev != null)
377                         prev.next = e.next;
378                     else
379                         tab[index] = e.next;
380 
381                     count--;
382                     Object oldValue = e.value;
383                     e.value = null;
384                     return oldValue;
385                 }
386             }
387         } else {
388             for (Entry e = tab[0], prev = null; e != null;
389                  prev = e, e = e.next) {
390                 if (e.key == null) {
391                     modCount++;
392                     if (prev != null)
393                         prev.next = e.next;
394                     else
395                         tab[0] = e.next;
396 
397                     count--;
398                     Object oldValue = e.value;
399                     e.value = null;
400                     return oldValue;
401                 }
402             }
403         }
404 
405         return null;
406     }
407 
408     /***
409      * Copies all of the mappings from the specified map to this one.
410      * 
411      * These mappings replace any mappings that this map had for any of the
412      * keys currently in the specified Map.
413      *
414      * @param t mappings to be stored in this map
415      */
416     public void putAll(Map t) {
417         Iterator i = t.entrySet().iterator();
418         while (i.hasNext()) {
419             Map.Entry e = (Map.Entry) i.next();
420             put(e.getKey(), e.getValue());
421         }
422     }
423 
424     /***
425      * Removes all mappings from this map.
426      */
427     public void clear() {
428         Entry tab[] = table;
429         modCount++;
430         for (int index = tab.length; --index >= 0; )
431             tab[index] = null;
432         count = 0;
433     }
434 
435     /***
436      * Returns a shallow copy of this <code>IdentityHashMap</code> instance: the keys and
437      * values themselves are not cloned.
438      *
439      * @return a shallow copy of this map
440      */
441     public Object clone() {
442         try { 
443             IdentityHashMap t = (IdentityHashMap)super.clone();
444             t.table = new Entry[table.length];
445             for (int i = table.length ; i-- > 0 ; ) {
446                 t.table[i] = (table[i] != null) 
447                     ? (Entry)table[i].clone() : null;
448             }
449             t.keySet = null;
450             t.entrySet = null;
451             t.values = null;
452             t.modCount = 0;
453             return t;
454         } catch (CloneNotSupportedException e) { 
455             // this shouldn't happen, since we are Cloneable
456             throw new InternalError();
457         }
458     }
459 
460     // Views
461 
462     private transient Set keySet = null;
463     private transient Set entrySet = null;
464     private transient Collection values = null;
465 
466     /***
467      * Returns a set view of the keys contained in this map.  The set is
468      * backed by the map, so changes to the map are reflected in the set, and
469      * vice-versa.  The set supports element removal, which removes the
470      * corresponding mapping from this map, via the <code>Iterator.remove</code>,
471      * <code>Set.remove</code>, <code>removeAll</code>, <code>retainAll</code>, and
472      * <code>clear</code> operations.  It does not support the <code>add</code> or
473      * <code>addAll</code> operations.
474      *
475      * @return a set view of the keys contained in this map
476      */
477     public Set keySet() {
478         if (keySet == null) {
479             keySet = new AbstractSet() {
480                 public Iterator iterator() {
481                     return getHashIterator(KEYS);
482                 }
483                 public int size() {
484                     return count;
485                 }
486                 public boolean contains(Object o) {
487                     return containsKey(o);
488                 }
489                 public boolean remove(Object o) {
490                     int oldSize = count;
491                     IdentityHashMap.this.remove(o);
492                     return count != oldSize;
493                 }
494                 public void clear() {
495                     IdentityHashMap.this.clear();
496                 }
497             };
498         }
499         return keySet;
500     }
501 
502     /***
503      * Returns a collection view of the values contained in this map.  The
504      * collection is backed by the map, so changes to the map are reflected in
505      * the collection, and vice-versa.  The collection supports element
506      * removal, which removes the corresponding mapping from this map, via the
507      * <code>Iterator.remove</code>, <code>Collection.remove</code>,
508      * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code> operations.
509      * It does not support the <code>add</code> or <code>addAll</code> operations.
510      *
511      * @return a collection view of the values contained in this map
512      */
513     public Collection values() {
514         if (values==null) {
515             values = new AbstractCollection() {
516                 public Iterator iterator() {
517                     return getHashIterator(VALUES);
518                 }
519                 public int size() {
520                     return count;
521                 }
522                 public boolean contains(Object o) {
523                     return containsValue(o);
524                 }
525                 public void clear() {
526                     IdentityHashMap.this.clear();
527                 }
528             };
529         }
530         return values;
531     }
532 
533     /***
534      * Returns a collection view of the mappings contained in this map.  Each
535      * element in the returned collection is a <code>Map.Entry</code>.  The
536      * collection is backed by the map, so changes to the map are reflected in
537      * the collection, and vice-versa.  The collection supports element
538      * removal, which removes the corresponding mapping from the map, via the
539      * <code>Iterator.remove</code>, <code>Collection.remove</code>,
540      * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code> operations.
541      * It does not support the <code>add</code> or <code>addAll</code> operations.
542      *
543      * @return a collection view of the mappings contained in this map
544      * @see Map.Entry
545      */
546     public Set entrySet() {
547         if (entrySet==null) {
548             entrySet = new AbstractSet() {
549                 public Iterator iterator() {
550                     return getHashIterator(ENTRIES);
551                 }
552 
553                 public boolean contains(Object o) {
554                     if (!(o instanceof Map.Entry))
555                         return false;
556                     Map.Entry entry = (Map.Entry)o;
557                     Object key = entry.getKey();
558                     Entry tab[] = table;
559                     // int hash = (key==null ? 0 : key.hashCode());
560                     int hash = (key==null ? 0 : System.identityHashCode( key ) );
561                     int index = (hash & 0x7FFFFFFF) % tab.length;
562 
563                     for (Entry e = tab[index]; e != null; e = e.next)
564                         if (e.hash==hash && e.equals(entry))
565                             return true;
566                     return false;
567                 }
568 
569                 public boolean remove(Object o) {
570                     if (!(o instanceof Map.Entry))
571                         return false;
572                     Map.Entry entry = (Map.Entry)o;
573                     Object key = entry.getKey();
574                     Entry tab[] = table;
575                     // int hash = (key==null ? 0 : key.hashCode());
576                     int hash = (key==null ? 0 : System.identityHashCode( key ) );
577                     int index = (hash & 0x7FFFFFFF) % tab.length;
578 
579                     for (Entry e = tab[index], prev = null; e != null;
580                          prev = e, e = e.next) {
581                         if (e.hash==hash && e.equals(entry)) {
582                             modCount++;
583                             if (prev != null)
584                                 prev.next = e.next;
585                             else
586                                 tab[index] = e.next;
587 
588                             count--;
589                             e.value = null;
590                             return true;
591                         }
592                     }
593                     return false;
594                 }
595 
596                 public int size() {
597                     return count;
598                 }
599 
600                 public void clear() {
601                     IdentityHashMap.this.clear();
602                 }
603             };
604         }
605 
606         return entrySet;
607     }
608 
609     private Iterator getHashIterator(int type) {
610         if (count == 0) {
611             return emptyHashIterator;
612         } else {
613             return new HashIterator(type);
614         }
615     }
616 
617     /***
618      * IdentityHashMap collision list entry.
619      */
620     private static class Entry implements Map.Entry {
621         int hash;
622         Object key;
623         Object value;
624         Entry next;
625 
626         Entry(int hash, Object key, Object value, Entry next) {
627             this.hash = hash;
628             this.key = key;
629             this.value = value;
630             this.next = next;
631         }
632 
633         protected Object clone() {
634             return new Entry(hash, key, value,
635                              (next==null ? null : (Entry)next.clone()));
636         }
637 
638         // Map.Entry Ops 
639 
640         public Object getKey() {
641             return key;
642         }
643 
644         public Object getValue() {
645             return value;
646         }
647 
648         public Object setValue(Object value) {
649             Object oldValue = this.value;
650             this.value = value;
651             return oldValue;
652         }
653 
654         public boolean equals(Object o) {
655             if (!(o instanceof Map.Entry))
656                 return false;
657             Map.Entry e = (Map.Entry)o;
658 
659             return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
660                (value==null ? e.getValue()==null : value.equals(e.getValue()));
661         }
662 
663         public int hashCode() {
664             return hash ^ (value==null ? 0 : value.hashCode());
665         }
666 
667         public String toString() {
668             return key+"="+value;
669         }
670     }
671 
672     // Types of Iterators
673     private static final int KEYS = 0;
674     private static final int VALUES = 1;
675     private static final int ENTRIES = 2;
676 
677     private static EmptyHashIterator emptyHashIterator 
678         = new EmptyHashIterator();
679                                              
680     private static class EmptyHashIterator implements Iterator {
681         
682         EmptyHashIterator() {
683             
684         }
685 
686         public boolean hasNext() {
687             return false;
688         }
689 
690         public Object next() {
691             throw new NoSuchElementException();
692         }
693         
694         public void remove() {
695             throw new IllegalStateException();
696         }
697 
698     }                   
699                     
700     private class HashIterator implements Iterator {
701         Entry[] table = IdentityHashMap.this.table;
702         int index = table.length;
703         Entry entry = null;
704         Entry lastReturned = null;
705         int type;
706 
707         /***
708          * The modCount value that the iterator believes that the backing
709          * List should have.  If this expectation is violated, the iterator
710          * has detected concurrent modification.
711          */
712         private int expectedModCount = modCount;
713 
714         HashIterator(int type) {
715             this.type = type;
716         }
717 
718         public boolean hasNext() {
719             Entry e = entry;
720             int i = index;
721             Entry t[] = table;
722             /* Use locals for faster loop iteration */
723             while (e == null && i > 0)
724                 e = t[--i];
725             entry = e;
726             index = i;
727             return e != null;
728         }
729 
730         public Object next() {
731             if (modCount != expectedModCount)
732                 throw new ConcurrentModificationException();
733 
734             Entry et = entry;
735             int i = index;
736             Entry t[] = table;
737 
738             /* Use locals for faster loop iteration */
739             while (et == null && i > 0) 
740                 et = t[--i];
741 
742             entry = et;
743             index = i;
744             if (et != null) {
745                 Entry e = lastReturned = entry;
746                 entry = e.next;
747                 return type == KEYS ? e.key : (type == VALUES ? e.value : e);
748             }
749             throw new NoSuchElementException();
750         }
751 
752         public void remove() {
753             if (lastReturned == null)
754                 throw new IllegalStateException();
755             if (modCount != expectedModCount)
756                 throw new ConcurrentModificationException();
757 
758             Entry[] tab = IdentityHashMap.this.table;
759             int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
760 
761             for (Entry e = tab[index], prev = null; e != null;
762                  prev = e, e = e.next) {
763                 if (e == lastReturned) {
764                     modCount++;
765                     expectedModCount++;
766                     if (prev == null)
767                         tab[index] = e.next;
768                     else
769                         prev.next = e.next;
770                     count--;
771                     lastReturned = null;
772                     return;
773                 }
774             }
775             throw new ConcurrentModificationException();
776         }
777     }
778 
779     /***
780      * Save the state of the <code>IdentityHashMap</code> instance to a stream (i.e.,
781      * serialize it).
782      *
783      * @serialData the <em>capacity</em> of the IdentityHashMap (the length of the
784      *             bucket array) is emitted (int), followed  by the
785      *             <em>size</em> of the IdentityHashMap (the number of key-value
786      *             mappings), followed by the key (Object) and value (Object)
787      *             for each key-value mapping represented by the IdentityHashMap.
788      *             The key-value mappings are emitted in no particular order.
789      */
790     private void writeObject(java.io.ObjectOutputStream s)
791         throws IOException
792     {
793         // Write out the threshold, load factor, and any hidden stuff
794         s.defaultWriteObject();
795 
796         // Write out number of buckets
797         s.writeInt(table.length);
798 
799         // Write out size (number of Mappings)
800         s.writeInt(count);
801 
802         // Write out keys and values (alternating)
803         for (int index = table.length-1; index >= 0; index--) {
804             Entry entry = table[index];
805 
806             while (entry != null) {
807                 s.writeObject(entry.key);
808                 s.writeObject(entry.value);
809                 entry = entry.next;
810             }
811         }
812     }
813 
814     private static final long serialVersionUID = 362498820763181265L;
815 
816     /***
817      * Reconstitute the <code>IdentityHashMap</code> instance from a stream (i.e.,
818      * deserialize it).
819      */
820     private void readObject(java.io.ObjectInputStream s)
821          throws IOException, ClassNotFoundException
822     {
823         // Read in the threshold, loadfactor, and any hidden stuff
824         s.defaultReadObject();
825 
826         // Read in number of buckets and allocate the bucket array;
827         int numBuckets = s.readInt();
828         table = new Entry[numBuckets];
829 
830         // Read in size (number of Mappings)
831         int size = s.readInt();
832 
833         // Read the keys and values, and put the mappings in the IdentityHashMap
834         for (int i=0; i<size; i++) {
835             Object key = s.readObject();
836             Object value = s.readObject();
837             put(key, value);
838         }
839     }
840 
841     int capacity() {
842         return table.length;
843     }
844 
845     float loadFactor() {
846         return loadFactor;
847     }
848 }