source: trunk/gcc/libjava/java/util/IdentityHashMap.java

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 28.0 KB
Line 
1/* IdentityHashMap.java -- a class providing a hashtable data structure,
2 mapping Object --> Object, which uses object identity for hashing.
3 Copyright (C) 2001, 2002 Free Software Foundation, Inc.
4
5This file is part of GNU Classpath.
6
7GNU Classpath is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Classpath is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Classpath; see the file COPYING. If not, write to the
19Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
2002111-1307 USA.
21
22Linking this library statically or dynamically with other modules is
23making a combined work based on this library. Thus, the terms and
24conditions of the GNU General Public License cover the whole
25combination.
26
27As a special exception, the copyright holders of this library give you
28permission to link this library with independent modules to produce an
29executable, regardless of the license terms of these independent
30modules, and to copy and distribute the resulting executable under
31terms of your choice, provided that you also meet, for each linked
32independent module, the terms and conditions of the license of that
33module. An independent module is a module which is not derived from
34or based on this library. If you modify this library, you may extend
35this exception to your version of the library, but you are not
36obligated to do so. If you do not wish to do so, delete this
37exception statement from your version. */
38
39package java.util;
40
41import java.io.*;
42
43/**
44 * This class provides a hashtable-backed implementation of the
45 * Map interface, but uses object identity to do its hashing. In fact,
46 * it uses object identity for comparing values, as well. It uses a
47 * linear-probe hash table, which may have faster performance
48 * than the chaining employed by HashMap.
49 * <p>
50 *
51 * <em>WARNING: This is not a general purpose map. Because it uses
52 * System.identityHashCode and ==, instead of hashCode and equals, for
53 * comparison, it violated Map's general contract, and may cause
54 * undefined behavior when compared to other maps which are not
55 * IdentityHashMaps. This is designed only for the rare cases when
56 * identity semantics are needed.</em> An example use is
57 * topology-preserving graph transformations, such as deep cloning,
58 * or as proxy object mapping such as in debugging.
59 * <p>
60 *
61 * This map permits <code>null</code> keys and values, and does not
62 * guarantee that elements will stay in the same order over time. The
63 * basic operations (<code>get</code> and <code>put</code>) take
64 * constant time, provided System.identityHashCode is decent. You can
65 * tune the behavior by specifying the expected maximum size. As more
66 * elements are added, the map may need to allocate a larger table,
67 * which can be expensive.
68 * <p>
69 *
70 * This implementation is unsynchronized. If you want multi-thread
71 * access to be consistent, you must synchronize it, perhaps by using
72 * <code>Collections.synchronizedMap(new IdentityHashMap(...));</code>.
73 * The iterators are <i>fail-fast</i>, meaning that a structural modification
74 * made to the map outside of an iterator's remove method cause the
75 * iterator, and in the case of the entrySet, the Map.Entry, to
76 * fail with a {@link ConcurrentModificationException}.
77 *
78 * @author Tom Tromey <tromey@redhat.com>
79 * @author Eric Blake <ebb9@email.byu.edu>
80 * @see System#identityHashCode(Object)
81 * @see Collection
82 * @see Map
83 * @see HashMap
84 * @see TreeMap
85 * @see LinkedHashMap
86 * @see WeakHashMap
87 * @since 1.4
88 * @status updated to 1.4
89 */
90public class IdentityHashMap extends AbstractMap
91 implements Map, Serializable, Cloneable
92{
93 /** The default capacity. */
94 private static final int DEFAULT_CAPACITY = 21;
95
96 /**
97 * This object is used to mark deleted items. Package visible for use by
98 * nested classes.
99 */
100 static final Object tombstone = new Object();
101
102 /**
103 * This object is used to mark empty slots. We need this because
104 * using null is ambiguous. Package visible for use by nested classes.
105 */
106 static final Object emptyslot = new Object();
107
108 /**
109 * Compatible with JDK 1.4.
110 */
111 private static final long serialVersionUID = 8188218128353913216L;
112
113 /**
114 * The number of mappings in the table. Package visible for use by nested
115 * classes.
116 * @serial
117 */
118 int size;
119
120 /**
121 * The table itself. Package visible for use by nested classes.
122 */
123 transient Object[] table;
124
125 /**
126 * The number of structural modifications made so far. Package visible for
127 * use by nested classes.
128 */
129 transient int modCount;
130
131 /**
132 * The cache for {@link #entrySet()}.
133 */
134 private transient Set entries;
135
136 /**
137 * The threshold for rehashing, which is 75% of (table.length / 2).
138 */
139 private transient int threshold;
140
141 /**
142 * Create a new IdentityHashMap with the default capacity (21 entries).
143 */
144 public IdentityHashMap()
145 {
146 this(DEFAULT_CAPACITY);
147 }
148
149 /**
150 * Create a new IdentityHashMap with the indicated number of
151 * entries. If the number of elements added to this hash map
152 * exceeds this maximum, the map will grow itself; however, that
153 * incurs a performance penalty.
154 *
155 * @param max initial size
156 * @throws IllegalArgumentException if max is negative
157 */
158 public IdentityHashMap(int max)
159 {
160 if (max < 0)
161 throw new IllegalArgumentException();
162 // Need at least two slots, or hash() will break.
163 if (max < 2)
164 max = 2;
165 table = new Object[max << 1];
166 Arrays.fill(table, emptyslot);
167 threshold = (max >> 2) * 3;
168 }
169
170 /**
171 * Create a new IdentityHashMap whose contents are taken from the
172 * given Map.
173 *
174 * @param m The map whose elements are to be put in this map
175 * @throws NullPointerException if m is null
176 */
177 public IdentityHashMap(Map m)
178 {
179 this(Math.max(m.size() << 1, DEFAULT_CAPACITY));
180 putAll(m);
181 }
182
183 /**
184 * Remove all mappings from this map.
185 */
186 public void clear()
187 {
188 if (size != 0)
189 {
190 modCount++;
191 Arrays.fill(table, emptyslot);
192 size = 0;
193 }
194 }
195
196 /**
197 * Creates a shallow copy where keys and values are not cloned.
198 */
199 public Object clone()
200 {
201 try
202 {
203 IdentityHashMap copy = (IdentityHashMap) super.clone();
204 copy.table = (Object[]) table.clone();
205 copy.entries = null; // invalidate the cache
206 return copy;
207 }
208 catch (CloneNotSupportedException e)
209 {
210 // Can't happen.
211 return null;
212 }
213 }
214
215 /**
216 * Tests whether the specified key is in this map. Unlike normal Maps,
217 * this test uses <code>entry == key</code> instead of
218 * <code>entry == null ? key == null : entry.equals(key)</code>.
219 *
220 * @param key the key to look for
221 * @return true if the key is contained in the map
222 * @see #containsValue(Object)
223 * @see #get(Object)
224 */
225 public boolean containsKey(Object key)
226 {
227 return key == table[hash(key)];
228 }
229
230 /**
231 * Returns true if this HashMap contains the value. Unlike normal maps,
232 * this test uses <code>entry == value</code> instead of
233 * <code>entry == null ? value == null : entry.equals(value)</code>.
234 *
235 * @param value the value to search for in this HashMap
236 * @return true if at least one key maps to the value
237 * @see #containsKey(Object)
238 */
239 public boolean containsValue(Object value)
240 {
241 for (int i = table.length - 1; i > 0; i -= 2)
242 if (table[i] == value)
243 return true;
244 return false;
245 }
246
247 /**
248 * Returns a "set view" of this Map's entries. The set is backed by
249 * the Map, so changes in one show up in the other. The set supports
250 * element removal, but not element addition.
251 * <p>
252 *
253 * <em>The semantics of this set, and of its contained entries, are
254 * different from the contract of Set and Map.Entry in order to make
255 * IdentityHashMap work. This means that while you can compare these
256 * objects between IdentityHashMaps, comparing them with regular sets
257 * or entries is likely to have undefined behavior.</em> The entries
258 * in this set are reference-based, rather than the normal object
259 * equality. Therefore, <code>e1.equals(e2)</code> returns
260 * <code>e1.getKey() == e2.getKey() && e1.getValue() == e2.getValue()</code>,
261 * and <code>e.hashCode()</code> returns
262 * <code>System.identityHashCode(e.getKey()) ^
263 * System.identityHashCode(e.getValue())</code>.
264 * <p>
265 *
266 * Note that the iterators for all three views, from keySet(), entrySet(),
267 * and values(), traverse the Map in the same sequence.
268 *
269 * @return a set view of the entries
270 * @see #keySet()
271 * @see #values()
272 * @see Map.Entry
273 */
274 public Set entrySet()
275 {
276 if (entries == null)
277 entries = new AbstractSet()
278 {
279 public int size()
280 {
281 return size;
282 }
283
284 public Iterator iterator()
285 {
286 return new IdentityIterator(ENTRIES);
287 }
288
289 public void clear()
290 {
291 IdentityHashMap.this.clear();
292 }
293
294 public boolean contains(Object o)
295 {
296 if (! (o instanceof Map.Entry))
297 return false;
298 Map.Entry m = (Map.Entry) o;
299 return m.getValue() == table[hash(m.getKey()) + 1];
300 }
301
302 public int hashCode()
303 {
304 return IdentityHashMap.this.hashCode();
305 }
306
307 public boolean remove(Object o)
308 {
309 if (! (o instanceof Map.Entry))
310 return false;
311 Object key = ((Map.Entry) o).getKey();
312 int h = hash(key);
313 if (table[h] == key)
314 {
315 size--;
316 modCount++;
317 table[h] = tombstone;
318 table[h + 1] = tombstone;
319 return true;
320 }
321 return false;
322 }
323 };
324 return entries;
325 }
326
327 /**
328 * Compares two maps for equality. This returns true only if both maps
329 * have the same reference-identity comparisons. While this returns
330 * <code>this.entrySet().equals(m.entrySet())</code> as specified by Map,
331 * this will not work with normal maps, since the entry set compares
332 * with == instead of .equals.
333 *
334 * @param o the object to compare to
335 * @return true if it is equal
336 */
337 public boolean equals(Object o)
338 {
339 // Why did Sun specify this one? The superclass does the right thing.
340 return super.equals(o);
341 }
342
343 /**
344 * Return the value in this Map associated with the supplied key, or
345 * <code>null</code> if the key maps to nothing.
346 *
347 * <p>NOTE: Since the value could also be null, you must use
348 * containsKey to see if this key actually maps to something.
349 * Unlike normal maps, this tests for the key with <code>entry ==
350 * key</code> instead of <code>entry == null ? key == null :
351 * entry.equals(key)</code>.
352 *
353 * @param key the key for which to fetch an associated value
354 * @return what the key maps to, if present
355 * @see #put(Object, Object)
356 * @see #containsKey(Object)
357 */
358 public Object get(Object key)
359 {
360 int h = hash(key);
361 return table[h] == key ? table[h + 1] : null;
362 }
363
364 /**
365 * Returns the hashcode of this map. This guarantees that two
366 * IdentityHashMaps that compare with equals() will have the same hash code,
367 * but may break with comparison to normal maps since it uses
368 * System.identityHashCode() instead of hashCode().
369 *
370 * @return the hash code
371 */
372 public int hashCode()
373 {
374 int hash = 0;
375 for (int i = table.length - 2; i >= 0; i -= 2)
376 {
377 Object key = table[i];
378 if (key == emptyslot || key == tombstone)
379 continue;
380 hash += (System.identityHashCode(key)
381 ^ System.identityHashCode(table[i + 1]));
382 }
383 return hash;
384 }
385
386 /**
387 * Returns true if there are no key-value mappings currently in this Map
388 * @return <code>size() == 0</code>
389 */
390 public boolean isEmpty()
391 {
392 return size == 0;
393 }
394
395 /**
396 * Returns a "set view" of this Map's keys. The set is backed by the
397 * Map, so changes in one show up in the other. The set supports
398 * element removal, but not element addition.
399 * <p>
400 *
401 * <em>The semantics of this set are different from the contract of Set
402 * in order to make IdentityHashMap work. This means that while you can
403 * compare these objects between IdentityHashMaps, comparing them with
404 * regular sets is likely to have undefined behavior.</em> The hashCode
405 * of the set is the sum of the identity hash codes, instead of the
406 * regular hashCodes, and equality is determined by reference instead
407 * of by the equals method.
408 * <p>
409 *
410 * @return a set view of the keys
411 * @see #values()
412 * @see #entrySet()
413 */
414 public Set keySet()
415 {
416 if (keys == null)
417 keys = new AbstractSet()
418 {
419 public int size()
420 {
421 return size;
422 }
423
424 public Iterator iterator()
425 {
426 return new IdentityIterator(KEYS);
427 }
428
429 public void clear()
430 {
431 IdentityHashMap.this.clear();
432 }
433
434 public boolean contains(Object o)
435 {
436 return containsKey(o);
437 }
438
439 public int hashCode()
440 {
441 int hash = 0;
442 for (int i = table.length - 2; i >= 0; i -= 2)
443 {
444 Object key = table[i];
445 if (key == emptyslot || key == tombstone)
446 continue;
447 hash += System.identityHashCode(key);
448 }
449 return hash;
450
451 }
452
453 public boolean remove(Object o)
454 {
455 int h = hash(o);
456 if (table[h] == o)
457 {
458 size--;
459 modCount++;
460 table[h] = tombstone;
461 table[h + 1] = tombstone;
462 return true;
463 }
464 return false;
465 }
466 };
467 return keys;
468 }
469
470 /**
471 * Puts the supplied value into the Map, mapped by the supplied key.
472 * The value may be retrieved by any object which <code>equals()</code>
473 * this key. NOTE: Since the prior value could also be null, you must
474 * first use containsKey if you want to see if you are replacing the
475 * key's mapping. Unlike normal maps, this tests for the key
476 * with <code>entry == key</code> instead of
477 * <code>entry == null ? key == null : entry.equals(key)</code>.
478 *
479 * @param key the key used to locate the value
480 * @param value the value to be stored in the HashMap
481 * @return the prior mapping of the key, or null if there was none
482 * @see #get(Object)
483 */
484 public Object put(Object key, Object value)
485 {
486 // Rehash if the load factor is too high.
487 if (size > threshold)
488 {
489 Object[] old = table;
490 // This isn't necessarily prime, but it is an odd number of key/value
491 // slots, which has a higher probability of fewer collisions.
492 table = new Object[old.length << 1 + 2];
493 Arrays.fill(table, emptyslot);
494 size = 0;
495 threshold = (table.length >>> 3) * 3;
496
497 for (int i = old.length - 2; i >= 0; i -= 2)
498 {
499 Object oldkey = old[i];
500 if (oldkey != tombstone && oldkey != emptyslot)
501 // Just use put. This isn't very efficient, but it is ok.
502 put(oldkey, old[i + 1]);
503 }
504 }
505
506 int h = hash(key);
507 if (table[h] == key)
508 {
509 Object r = table[h + 1];
510 table[h + 1] = value;
511 return r;
512 }
513
514 // At this point, we add a new mapping.
515 modCount++;
516 size++;
517 table[h] = key;
518 table[h + 1] = value;
519 return null;
520 }
521
522 /**
523 * Copies all of the mappings from the specified map to this. If a key
524 * is already in this map, its value is replaced.
525 *
526 * @param m the map to copy
527 * @throws NullPointerException if m is null
528 */
529 public void putAll(Map m)
530 {
531 // Why did Sun specify this one? The superclass does the right thing.
532 super.putAll(m);
533 }
534
535 /**
536 * Removes from the HashMap and returns the value which is mapped by
537 * the supplied key. If the key maps to nothing, then the HashMap
538 * remains unchanged, and <code>null</code> is returned.
539 *
540 * NOTE: Since the value could also be null, you must use
541 * containsKey to see if you are actually removing a mapping.
542 * Unlike normal maps, this tests for the key with <code>entry ==
543 * key</code> instead of <code>entry == null ? key == null :
544 * entry.equals(key)</code>.
545 *
546 * @param key the key used to locate the value to remove
547 * @return whatever the key mapped to, if present
548 */
549 public Object remove(Object key)
550 {
551 int h = hash(key);
552 if (table[h] == key)
553 {
554 modCount++;
555 size--;
556 Object r = table[h + 1];
557 table[h] = tombstone;
558 table[h + 1] = tombstone;
559 return r;
560 }
561 return null;
562 }
563
564 /**
565 * Returns the number of kay-value mappings currently in this Map
566 * @return the size
567 */
568 public int size()
569 {
570 return size;
571 }
572
573 /**
574 * Returns a "collection view" (or "bag view") of this Map's values.
575 * The collection is backed by the Map, so changes in one show up
576 * in the other. The collection supports element removal, but not element
577 * addition.
578 * <p>
579 *
580 * <em>The semantics of this set are different from the contract of
581 * Collection in order to make IdentityHashMap work. This means that
582 * while you can compare these objects between IdentityHashMaps, comparing
583 * them with regular sets is likely to have undefined behavior.</em>
584 * Likewise, contains and remove go by == instead of equals().
585 * <p>
586 *
587 * @return a bag view of the values
588 * @see #keySet()
589 * @see #entrySet()
590 */
591 public Collection values()
592 {
593 if (values == null)
594 values = new AbstractCollection()
595 {
596 public int size()
597 {
598 return size;
599 }
600
601 public Iterator iterator()
602 {
603 return new IdentityIterator(VALUES);
604 }
605
606 public void clear()
607 {
608 IdentityHashMap.this.clear();
609 }
610
611 public boolean remove(Object o)
612 {
613 for (int i = table.length - 1; i > 0; i -= 2)
614 if (table[i] == o)
615 {
616 modCount++;
617 table[i - 1] = tombstone;
618 table[i] = tombstone;
619 size--;
620 return true;
621 }
622 return false;
623 }
624 };
625 return values;
626 }
627
628 /**
629 * Helper method which computes the hash code, then traverses the table
630 * until it finds the key, or the spot where the key would go.
631 *
632 * @param key the key to check
633 * @return the index where the key belongs
634 * @see #IdentityHashMap(int)
635 * @see #put(Object, Object)
636 */
637 // Package visible for use by nested classes.
638 int hash(Object key)
639 {
640 // Implementation note: it is feasible for the table to have no
641 // emptyslots, if it is full with entries and tombstones, so we must
642 // remember where we started. If we encounter the key or an emptyslot,
643 // we are done. If we encounter a tombstone, the key may still be in
644 // the array. If we don't encounter the key, we use the first emptyslot
645 // or tombstone we encountered as the location where the key would go.
646 // By requiring at least 2 key/value slots, and rehashing at 75%
647 // capacity, we guarantee that there will always be either an emptyslot
648 // or a tombstone somewhere in the table.
649 int h = Math.abs(System.identityHashCode(key) % (table.length >> 1)) << 1;
650 int del = -1;
651 int save = h;
652
653 do
654 {
655 if (table[h] == key)
656 return h;
657 if (table[h] == emptyslot)
658 break;
659 if (table[h] == tombstone && del < 0)
660 del = h;
661 h -= 2;
662 if (h < 0)
663 h = table.length - 2;
664 }
665 while (h != save);
666
667 return del < 0 ? h : del;
668 }
669
670 /**
671 * This class allows parameterized iteration over IdentityHashMaps. Based
672 * on its construction, it returns the key or value of a mapping, or
673 * creates the appropriate Map.Entry object with the correct fail-fast
674 * semantics and identity comparisons.
675 *
676 * @author Tom Tromey <tromey@redhat.com>
677 * @author Eric Blake <ebb9@email.byu.edu>
678 */
679 private final class IdentityIterator implements Iterator
680 {
681 /**
682 * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
683 * or {@link #ENTRIES}.
684 */
685 final int type;
686 /** The number of modifications to the backing Map that we know about. */
687 int knownMod = modCount;
688 /** The number of elements remaining to be returned by next(). */
689 int count = size;
690 /** Location in the table. */
691 int loc = table.length;
692
693 /**
694 * Construct a new Iterator with the supplied type.
695 * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
696 */
697 IdentityIterator(int type)
698 {
699 this.type = type;
700 }
701
702 /**
703 * Returns true if the Iterator has more elements.
704 * @return true if there are more elements
705 * @throws ConcurrentModificationException if the Map was modified
706 */
707 public boolean hasNext()
708 {
709 if (knownMod != modCount)
710 throw new ConcurrentModificationException();
711 return count > 0;
712 }
713
714 /**
715 * Returns the next element in the Iterator's sequential view.
716 * @return the next element
717 * @throws ConcurrentModificationException if the Map was modified
718 * @throws NoSuchElementException if there is none
719 */
720 public Object next()
721 {
722 if (knownMod != modCount)
723 throw new ConcurrentModificationException();
724 if (count == 0)
725 throw new NoSuchElementException();
726 count--;
727
728 Object key;
729 do
730 {
731 loc -= 2;
732 key = table[loc];
733 }
734 while (key == emptyslot || key == tombstone);
735
736 return type == KEYS ? key : (type == VALUES ? table[loc + 1]
737 : new IdentityEntry(loc));
738 }
739
740 /**
741 * Removes from the backing Map the last element which was fetched
742 * with the <code>next()</code> method.
743 *
744 * @throws ConcurrentModificationException if the Map was modified
745 * @throws IllegalStateException if called when there is no last element
746 */
747 public void remove()
748 {
749 if (knownMod != modCount)
750 throw new ConcurrentModificationException();
751 if (loc == table.length || table[loc] == tombstone)
752 throw new IllegalStateException();
753 modCount++;
754 size--;
755 table[loc] = tombstone;
756 table[loc + 1] = tombstone;
757 knownMod++;
758 }
759 } // class IdentityIterator
760
761 /**
762 * This class provides Map.Entry objects for IdentityHashMaps. The entry
763 * is fail-fast, and will throw a ConcurrentModificationException if
764 * the underlying map is modified, or if remove is called on the iterator
765 * that generated this object. It is identity based, so it violates
766 * the general contract of Map.Entry, and is probably unsuitable for
767 * comparison to normal maps; but it works among other IdentityHashMaps.
768 *
769 * @author Eric Blake <ebb9@email.byu.edu>
770 */
771 private final class IdentityEntry implements Map.Entry
772 {
773 /** The location of this entry. */
774 final int loc;
775 /** The number of modifications to the backing Map that we know about. */
776 final int knownMod = modCount;
777
778 /**
779 * Constructs the Entry.
780 *
781 * @param loc the location of this entry in table
782 */
783 IdentityEntry(int loc)
784 {
785 this.loc = loc;
786 }
787
788 /**
789 * Compares the specified object with this entry, using identity
790 * semantics. Note that this can lead to undefined results with
791 * Entry objects created by normal maps.
792 *
793 * @param o the object to compare
794 * @return true if it is equal
795 * @throws ConcurrentModificationException if the entry was invalidated
796 * by modifying the Map or calling Iterator.remove()
797 */
798 public boolean equals(Object o)
799 {
800 if (knownMod != modCount || table[loc] == tombstone)
801 throw new ConcurrentModificationException();
802 if (! (o instanceof Map.Entry))
803 return false;
804 Map.Entry e = (Map.Entry) o;
805 return table[loc] == e.getKey() && table[loc + 1] == e.getValue();
806 }
807
808 /**
809 * Returns the key of this entry.
810 *
811 * @return the key
812 * @throws ConcurrentModificationException if the entry was invalidated
813 * by modifying the Map or calling Iterator.remove()
814 */
815 public Object getKey()
816 {
817 if (knownMod != modCount || table[loc] == tombstone)
818 throw new ConcurrentModificationException();
819 return table[loc];
820 }
821
822 /**
823 * Returns the value of this entry.
824 *
825 * @return the value
826 * @throws ConcurrentModificationException if the entry was invalidated
827 * by modifying the Map or calling Iterator.remove()
828 */
829 public Object getValue()
830 {
831 if (knownMod != modCount || table[loc] == tombstone)
832 throw new ConcurrentModificationException();
833 return table[loc + 1];
834 }
835
836 /**
837 * Returns the hashcode of the entry, using identity semantics.
838 * Note that this can lead to undefined results with Entry objects
839 * created by normal maps.
840 *
841 * @return the hash code
842 * @throws ConcurrentModificationException if the entry was invalidated
843 * by modifying the Map or calling Iterator.remove()
844 */
845 public int hashCode()
846 {
847 if (knownMod != modCount || table[loc] == tombstone)
848 throw new ConcurrentModificationException();
849 return (System.identityHashCode(table[loc])
850 ^ System.identityHashCode(table[loc + 1]));
851 }
852
853 /**
854 * Replaces the value of this mapping, and returns the old value.
855 *
856 * @param value the new value
857 * @return the old value
858 * @throws ConcurrentModificationException if the entry was invalidated
859 * by modifying the Map or calling Iterator.remove()
860 */
861 public Object setValue(Object value)
862 {
863 if (knownMod != modCount || table[loc] == tombstone)
864 throw new ConcurrentModificationException();
865 Object r = table[loc + 1];
866 table[loc + 1] = value;
867 return r;
868 }
869
870 /**
871 * This provides a string representation of the entry. It is of the form
872 * "key=value", where string concatenation is used on key and value.
873 *
874 * @return the string representation
875 * @throws ConcurrentModificationException if the entry was invalidated
876 * by modifying the Map or calling Iterator.remove()
877 */
878 public final String toString()
879 {
880 if (knownMod != modCount || table[loc] == tombstone)
881 throw new ConcurrentModificationException();
882 return table[loc] + "=" + table[loc + 1];
883 }
884 } // class IdentityEntry
885
886 /**
887 * Reads the object from a serial stream.
888 *
889 * @param s the stream to read from
890 * @throws ClassNotFoundException if the underlying stream fails
891 * @throws IOException if the underlying stream fails
892 * @serialData expects the size (int), followed by that many key (Object)
893 * and value (Object) pairs, with the pairs in no particular
894 * order
895 */
896 private void readObject(ObjectInputStream s)
897 throws IOException, ClassNotFoundException
898 {
899 s.defaultReadObject();
900
901 int num = s.readInt();
902 table = new Object[Math.max(num << 1, DEFAULT_CAPACITY) << 1];
903 // Read key/value pairs.
904 while (--num >= 0)
905 put(s.readObject(), s.readObject());
906 }
907
908 /**
909 * Writes the object to a serial stream.
910 *
911 * @param s the stream to write to
912 * @throws IOException if the underlying stream fails
913 * @serialData outputs the size (int), followed by that many key (Object)
914 * and value (Object) pairs, with the pairs in no particular
915 * order
916 */
917 private void writeObject(ObjectOutputStream s)
918 throws IOException
919 {
920 s.defaultWriteObject();
921 s.writeInt(size);
922 for (int i = table.length - 2; i >= 0; i -= 2)
923 {
924 Object key = table[i];
925 if (key != tombstone && key != emptyslot)
926 {
927 s.writeObject(key);
928 s.writeObject(table[i + 1]);
929 }
930 }
931 }
932}
Note: See TracBrowser for help on using the repository browser.