001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.ext.util;
054:
055: import java.util.AbstractCollection;
056: import java.util.AbstractMap;
057: import java.util.AbstractSet;
058: import java.util.Collection;
059: import java.util.ConcurrentModificationException;
060: import java.util.Iterator;
061: import java.util.Map;
062: import java.util.NoSuchElementException;
063: import java.util.Set;
064:
065: /**
066: * A variant of {@link java.util.HashMap} that uses
067: * {@link System#identityHashCode(Object)} for hashing, and reference comparison
068: * instead of {@link Object#equals(Object)}. Note that this applies only to keys,
069: * and not to values, i.e. {@link #containsValue(Object)} still uses {@link Object#equals(Object)}.
070: * @author Attila Szegedi
071: */
072: public class IdentityHashMap extends AbstractMap implements Map,
073: Cloneable, java.io.Serializable {
074:
075: public static final long serialVersionUID = 362498820763181265L;
076: /**
077: * The hash table data.
078: */
079: private transient Entry table[];
080:
081: /**
082: * The total number of mappings in the hash table.
083: */
084: private transient int count;
085:
086: /**
087: * The table is rehashed when its size exceeds this threshold. (The
088: * value of this field is (int)(capacity * loadFactor).)
089: *
090: * @serial
091: */
092: private int threshold;
093:
094: /**
095: * The load factor for the hashtable.
096: *
097: * @serial
098: */
099: private float loadFactor;
100:
101: /**
102: * The number of times this IdentityHashMap has been structurally modified
103: * Structural modifications are those that change the number of mappings in
104: * the IdentityHashMap or otherwise modify its internal structure (e.g.,
105: * rehash). This field is used to make iterators on Collection-views of
106: * the IdentityHashMap 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 IdentityHashMap.
115: * @param loadFactor the load factor of the IdentityHashMap
116: * @throws IllegalArgumentException if the initial capacity is less
117: * than zero, or if the load factor is nonpositive.
118: */
119: public IdentityHashMap(int initialCapacity, float loadFactor) {
120: if (initialCapacity < 0)
121: throw new IllegalArgumentException(
122: "Illegal Initial Capacity: " + 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 <tt>0.75</tt>.
136: *
137: * @param initialCapacity the initial capacity of the IdentityHashMap.
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 <tt>0.75</tt>.
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 <tt>0.75</tt>.
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 <tt>true</tt> if this map contains no key-value mappings.
177: *
178: * @return <tt>true</tt> if this map contains no key-value mappings.
179: */
180: public boolean isEmpty() {
181: return count == 0;
182: }
183:
184: /**
185: * Returns <tt>true</tt> 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 <tt>true</tt> 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;)
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;)
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 <tt>true</tt> if this map contains a mapping for the specified
212: * key.
213: *
214: * @return <tt>true</tt> 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 = System.identityHashCode(key);
222: int index = (hash & 0x7FFFFFFF) % tab.length;
223: for (Entry e = tab[index]; e != null; e = e.next)
224: if (e.hash == hash && key == e.key)
225: return true;
226: } else {
227: for (Entry e = tab[0]; e != null; e = e.next)
228: if (e.key == null)
229: return true;
230: }
231:
232: return false;
233: }
234:
235: /**
236: * Returns the value to which this map maps the specified key. Returns
237: * <tt>null</tt> if the map contains no mapping for this key. A return
238: * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
239: * map contains no mapping for the key; it's also possible that the map
240: * explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt>
241: * operation may be used to distinguish these two cases.
242: *
243: * @return the value to which this map maps the specified key.
244: * @param key key whose associated value is to be returned.
245: */
246: public Object get(Object key) {
247: Entry tab[] = table;
248:
249: if (key != null) {
250: int hash = System.identityHashCode(key);
251: int index = (hash & 0x7FFFFFFF) % tab.length;
252: for (Entry e = tab[index]; e != null; e = e.next)
253: if ((e.hash == hash) && key == e.key)
254: return e.value;
255: } else {
256: for (Entry e = tab[0]; e != null; e = e.next)
257: if (e.key == null)
258: return e.value;
259: }
260:
261: return null;
262: }
263:
264: /**
265: * Rehashes the contents of this map into a new <tt>IdentityHashMap</tt> instance
266: * with a larger capacity. This method is called automatically when the
267: * number of keys in this map exceeds its capacity and load factor.
268: */
269: private void rehash() {
270: int oldCapacity = table.length;
271: Entry oldMap[] = table;
272:
273: int newCapacity = oldCapacity * 2 + 1;
274: Entry newMap[] = new Entry[newCapacity];
275:
276: modCount++;
277: threshold = (int) (newCapacity * loadFactor);
278: table = newMap;
279:
280: for (int i = oldCapacity; i-- > 0;) {
281: for (Entry old = oldMap[i]; old != null;) {
282: Entry e = old;
283: old = old.next;
284:
285: int index = (e.hash & 0x7FFFFFFF) % newCapacity;
286: e.next = newMap[index];
287: newMap[index] = e;
288: }
289: }
290: }
291:
292: /**
293: * Associates the specified value with the specified key in this map.
294: * If the map previously contained a mapping for this key, the old
295: * value is replaced.
296: *
297: * @param key key with which the specified value is to be associated.
298: * @param value value to be associated with the specified key.
299: * @return previous value associated with specified key, or <tt>null</tt>
300: * if there was no mapping for key. A <tt>null</tt> return can
301: * also indicate that the IdentityHashMap previously associated
302: * <tt>null</tt> with the specified key.
303: */
304: public Object put(Object key, Object value) {
305: // Makes sure the key is not already in the IdentityHashMap.
306: Entry tab[] = table;
307: int hash = 0;
308: int index = 0;
309:
310: if (key != null) {
311: hash = System.identityHashCode(key);
312: index = (hash & 0x7FFFFFFF) % tab.length;
313: for (Entry e = tab[index]; e != null; e = e.next) {
314: if ((e.hash == hash) && key == e.key) {
315: Object old = e.value;
316: e.value = value;
317: return old;
318: }
319: }
320: } else {
321: for (Entry e = tab[0]; e != null; e = e.next) {
322: if (e.key == null) {
323: Object old = e.value;
324: e.value = value;
325: return old;
326: }
327: }
328: }
329:
330: modCount++;
331: if (count >= threshold) {
332: // Rehash the table if the threshold is exceeded
333: rehash();
334:
335: tab = table;
336: index = (hash & 0x7FFFFFFF) % tab.length;
337: }
338:
339: // Creates the new entry.
340: Entry e = new Entry(hash, key, value, tab[index]);
341: tab[index] = e;
342: count++;
343: return null;
344: }
345:
346: /**
347: * Removes the mapping for this key from this map if present.
348: *
349: * @param key key whose mapping is to be removed from the map.
350: * @return previous value associated with specified key, or <tt>null</tt>
351: * if there was no mapping for key. A <tt>null</tt> return can
352: * also indicate that the map previously associated <tt>null</tt>
353: * with the specified key.
354: */
355: public Object remove(Object key) {
356: Entry tab[] = table;
357:
358: if (key != null) {
359: int hash = System.identityHashCode(key);
360: int index = (hash & 0x7FFFFFFF) % tab.length;
361:
362: for (Entry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
363: if ((e.hash == hash) && key == e.key) {
364: modCount++;
365: if (prev != null)
366: prev.next = e.next;
367: else
368: tab[index] = e.next;
369:
370: count--;
371: Object oldValue = e.value;
372: e.value = null;
373: return oldValue;
374: }
375: }
376: } else {
377: for (Entry e = tab[0], prev = null; e != null; prev = e, e = e.next) {
378: if (e.key == null) {
379: modCount++;
380: if (prev != null)
381: prev.next = e.next;
382: else
383: tab[0] = e.next;
384:
385: count--;
386: Object oldValue = e.value;
387: e.value = null;
388: return oldValue;
389: }
390: }
391: }
392:
393: return null;
394: }
395:
396: /**
397: * Copies all of the mappings from the specified map to this one.
398: *
399: * These mappings replace any mappings that this map had for any of the
400: * keys currently in the specified Map.
401: *
402: * @param t Mappings to be stored in this map.
403: */
404: public void putAll(Map t) {
405: Iterator i = t.entrySet().iterator();
406: while (i.hasNext()) {
407: Map.Entry e = (Map.Entry) i.next();
408: put(e.getKey(), e.getValue());
409: }
410: }
411:
412: /**
413: * Removes all mappings from this map.
414: */
415: public void clear() {
416: Entry tab[] = table;
417: modCount++;
418: for (int index = tab.length; --index >= 0;)
419: tab[index] = null;
420: count = 0;
421: }
422:
423: /**
424: * Returns a shallow copy of this <tt>IdentityHashMap</tt> instance: the keys and
425: * values themselves are not cloned.
426: *
427: * @return a shallow copy of this map.
428: */
429: public Object clone() {
430: try {
431: IdentityHashMap t = (IdentityHashMap) super .clone();
432: t.table = new Entry[table.length];
433: for (int i = table.length; i-- > 0;) {
434: t.table[i] = (table[i] != null) ? (Entry) table[i]
435: .clone() : null;
436: }
437: t.keySet = null;
438: t.entrySet = null;
439: t.values = null;
440: t.modCount = 0;
441: return t;
442: } catch (CloneNotSupportedException e) {
443: // this shouldn't happen, since we are Cloneable
444: throw new InternalError();
445: }
446: }
447:
448: // Views
449:
450: private transient Set keySet = null;
451: private transient Set entrySet = null;
452: private transient Collection values = null;
453:
454: /**
455: * Returns a set view of the keys contained in this map. The set is
456: * backed by the map, so changes to the map are reflected in the set, and
457: * vice versa. The set supports element removal, which removes the
458: * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
459: * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
460: * <tt>clear</tt> operations. It does not support the <tt>add</tt> or
461: * <tt>addAll</tt> operations.
462: *
463: * @return a set view of the keys contained in this map.
464: */
465: public Set keySet() {
466: if (keySet == null) {
467: keySet = new AbstractSet() {
468: public Iterator iterator() {
469: return getHashIterator(KEYS);
470: }
471:
472: public int size() {
473: return count;
474: }
475:
476: public boolean contains(Object o) {
477: return containsKey(o);
478: }
479:
480: public boolean remove(Object o) {
481: int oldSize = count;
482: IdentityHashMap.this .remove(o);
483: return count != oldSize;
484: }
485:
486: public void clear() {
487: IdentityHashMap.this .clear();
488: }
489: };
490: }
491: return keySet;
492: }
493:
494: /**
495: * Returns a collection view of the values contained in this map. The
496: * collection is backed by the map, so changes to the map are reflected in
497: * the collection, and vice versa. The collection supports element
498: * removal, which removes the corresponding mapping from this map, via the
499: * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
500: * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
501: * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
502: *
503: * @return a collection view of the values contained in this map.
504: */
505: public Collection values() {
506: if (values == null) {
507: values = new AbstractCollection() {
508: public Iterator iterator() {
509: return getHashIterator(VALUES);
510: }
511:
512: public int size() {
513: return count;
514: }
515:
516: public boolean contains(Object o) {
517: return containsValue(o);
518: }
519:
520: public void clear() {
521: IdentityHashMap.this .clear();
522: }
523: };
524: }
525: return values;
526: }
527:
528: /**
529: * Returns a collection view of the mappings contained in this map. Each
530: * element in the returned collection is a <tt>Map.Entry</tt>. The
531: * collection is backed by the map, so changes to the map are reflected in
532: * the collection, and vice versa. The collection supports element
533: * removal, which removes the corresponding mapping from the map, via the
534: * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
535: * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
536: * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
537: *
538: * @return a collection view of the mappings contained in this map.
539: * @see java.util.Map.Entry
540: */
541: public Set entrySet() {
542: if (entrySet == null) {
543: entrySet = new AbstractSet() {
544: public Iterator iterator() {
545: return getHashIterator(ENTRIES);
546: }
547:
548: public boolean contains(Object o) {
549: if (!(o instanceof Map.Entry))
550: return false;
551: Map.Entry entry = (Map.Entry) o;
552: Object key = entry.getKey();
553: Entry tab[] = table;
554: int hash = (key == null ? 0 : System
555: .identityHashCode(key));
556: int index = (hash & 0x7FFFFFFF) % tab.length;
557:
558: for (Entry e = tab[index]; e != null; e = e.next)
559: if (e.hash == hash && e.equals(entry))
560: return true;
561: return false;
562: }
563:
564: public boolean remove(Object o) {
565: if (!(o instanceof Map.Entry))
566: return false;
567: Map.Entry entry = (Map.Entry) o;
568: Object key = entry.getKey();
569: Entry tab[] = table;
570: int hash = (key == null ? 0 : System
571: .identityHashCode(key));
572: int index = (hash & 0x7FFFFFFF) % tab.length;
573:
574: for (Entry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
575: if (e.hash == hash && e.equals(entry)) {
576: modCount++;
577: if (prev != null)
578: prev.next = e.next;
579: else
580: tab[index] = e.next;
581:
582: count--;
583: e.value = null;
584: return true;
585: }
586: }
587: return false;
588: }
589:
590: public int size() {
591: return count;
592: }
593:
594: public void clear() {
595: IdentityHashMap.this .clear();
596: }
597: };
598: }
599:
600: return entrySet;
601: }
602:
603: private Iterator getHashIterator(int type) {
604: if (count == 0) {
605: return emptyHashIterator;
606: } else {
607: return new HashIterator(type);
608: }
609: }
610:
611: /**
612: * IdentityHashMap collision list entry.
613: */
614: private static class Entry implements Map.Entry {
615: int hash;
616: Object key;
617: Object value;
618: Entry next;
619:
620: Entry(int hash, Object key, Object value, Entry next) {
621: this .hash = hash;
622: this .key = key;
623: this .value = value;
624: this .next = next;
625: }
626:
627: protected Object clone() {
628: return new Entry(hash, key, value, (next == null ? null
629: : (Entry) next.clone()));
630: }
631:
632: // Map.Entry Ops
633:
634: public Object getKey() {
635: return key;
636: }
637:
638: public Object getValue() {
639: return value;
640: }
641:
642: public Object setValue(Object value) {
643: Object oldValue = this .value;
644: this .value = value;
645: return oldValue;
646: }
647:
648: public boolean equals(Object o) {
649: if (!(o instanceof Map.Entry))
650: return false;
651: Map.Entry e = (Map.Entry) o;
652:
653: return (key == e.getKey())
654: && (value == null ? e.getValue() == null : value
655: .equals(e.getValue()));
656: }
657:
658: public int hashCode() {
659: return hash ^ (value == null ? 0 : value.hashCode());
660: }
661:
662: public String toString() {
663: return key + "=" + value;
664: }
665: }
666:
667: // Types of Iterators
668: private static final int KEYS = 0;
669: private static final int VALUES = 1;
670: private static final int ENTRIES = 2;
671:
672: private static EmptyHashIterator emptyHashIterator = new EmptyHashIterator();
673:
674: private static class EmptyHashIterator implements Iterator {
675:
676: EmptyHashIterator() {
677:
678: }
679:
680: public boolean hasNext() {
681: return false;
682: }
683:
684: public Object next() {
685: throw new NoSuchElementException();
686: }
687:
688: public void remove() {
689: throw new IllegalStateException();
690: }
691:
692: }
693:
694: private class HashIterator implements Iterator {
695: Entry[] table = IdentityHashMap.this .table;
696: int index = table.length;
697: Entry entry = null;
698: Entry lastReturned = null;
699: int type;
700:
701: /**
702: * The modCount value that the iterator believes that the backing
703: * List should have. If this expectation is violated, the iterator
704: * has detected concurrent modification.
705: */
706: private int expectedModCount = modCount;
707:
708: HashIterator(int type) {
709: this .type = type;
710: }
711:
712: public boolean hasNext() {
713: Entry e = entry;
714: int i = index;
715: Entry t[] = table;
716: /* Use locals for faster loop iteration */
717: while (e == null && i > 0)
718: e = t[--i];
719: entry = e;
720: index = i;
721: return e != null;
722: }
723:
724: public Object next() {
725: if (modCount != expectedModCount)
726: throw new ConcurrentModificationException();
727:
728: Entry et = entry;
729: int i = index;
730: Entry t[] = table;
731:
732: /* Use locals for faster loop iteration */
733: while (et == null && i > 0)
734: et = t[--i];
735:
736: entry = et;
737: index = i;
738: if (et != null) {
739: Entry e = lastReturned = entry;
740: entry = e.next;
741: return type == KEYS ? e.key : (type == VALUES ? e.value
742: : e);
743: }
744: throw new NoSuchElementException();
745: }
746:
747: public void remove() {
748: if (lastReturned == null)
749: throw new IllegalStateException();
750: if (modCount != expectedModCount)
751: throw new ConcurrentModificationException();
752:
753: Entry[] tab = IdentityHashMap.this .table;
754: int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
755:
756: for (Entry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
757: if (e == lastReturned) {
758: modCount++;
759: expectedModCount++;
760: if (prev == null)
761: tab[index] = e.next;
762: else
763: prev.next = e.next;
764: count--;
765: lastReturned = null;
766: return;
767: }
768: }
769: throw new ConcurrentModificationException();
770: }
771: }
772:
773: /**
774: * Save the state of the <tt>IdentityHashMap</tt> instance to a stream (i.e.,
775: * serialize it).
776: *
777: * @serialData The <i>capacity</i> of the IdentityHashMap (the length of the
778: * bucket array) is emitted (int), followed by the
779: * <i>size</i> of the IdentityHashMap (the number of key-value
780: * mappings), followed by the key (Object) and value (Object)
781: * for each key-value mapping represented by the IdentityHashMap
782: * The key-value mappings are emitted in no particular order.
783: */
784: private void writeObject(java.io.ObjectOutputStream s)
785: throws java.io.IOException {
786: // Write out the threshold, loadfactor, and any hidden stuff
787: s.defaultWriteObject();
788:
789: // Write out number of buckets
790: s.writeInt(table.length);
791:
792: // Write out size (number of Mappings)
793: s.writeInt(count);
794:
795: // Write out keys and values (alternating)
796: for (int index = table.length - 1; index >= 0; index--) {
797: Entry entry = table[index];
798:
799: while (entry != null) {
800: s.writeObject(entry.key);
801: s.writeObject(entry.value);
802: entry = entry.next;
803: }
804: }
805: }
806:
807: /**
808: * Reconstitute the <tt>IdentityHashMap</tt> instance from a stream (i.e.,
809: * deserialize it).
810: */
811: private void readObject(java.io.ObjectInputStream s)
812: throws java.io.IOException, ClassNotFoundException {
813: // Read in the threshold, loadfactor, and any hidden stuff
814: s.defaultReadObject();
815:
816: // Read in number of buckets and allocate the bucket array;
817: int numBuckets = s.readInt();
818: table = new Entry[numBuckets];
819:
820: // Read in size (number of Mappings)
821: int size = s.readInt();
822:
823: // Read the keys and values, and put the mappings in the IdentityHashMap
824: for (int i = 0; i < size; i++) {
825: Object key = s.readObject();
826: Object value = s.readObject();
827: put(key, value);
828: }
829: }
830:
831: int capacity() {
832: return table.length;
833: }
834:
835: float loadFactor() {
836: return loadFactor;
837: }
838: }
|