source: trunk/src/gcc/libjava/java/util/IdentityHashMap.java@ 192

Last change on this file since 192 was 2, checked in by bird, 23 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 27.9 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 <[email protected]>
79 * @author Eric Blake <[email protected]>
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[2 * max];
166 Arrays.fill(table, emptyslot);
167 threshold = max / 4 * 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() * 2, 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,
345 * or <pre>null</pre> if the key maps to nothing. NOTE: Since the value
346 * could also be null, you must use containsKey to see if this key
347 * actually maps to something. Unlike normal maps, this tests for the key
348 * with <code>entry == key</code> instead of
349 * <code>entry == null ? key == null : entry.equals(key)</code>.
350 *
351 * @param key the key for which to fetch an associated value
352 * @return what the key maps to, if present
353 * @see #put(Object, Object)
354 * @see #containsKey(Object)
355 */
356 public Object get(Object key)
357 {
358 int h = hash(key);
359 return table[h] == key ? table[h + 1] : null;
360 }
361
362 /**
363 * Returns the hashcode of this map. This guarantees that two
364 * IdentityHashMaps that compare with equals() will have the same hash code,
365 * but may break with comparison to normal maps since it uses
366 * System.identityHashCode() instead of hashCode().
367 *
368 * @return the hash code
369 */
370 public int hashCode()
371 {
372 int hash = 0;
373 for (int i = table.length - 2; i >= 0; i -= 2)
374 {
375 Object key = table[i];
376 if (key == emptyslot || key == tombstone)
377 continue;
378 hash += (System.identityHashCode(key)
379 ^ System.identityHashCode(table[i + 1]));
380 }
381 return hash;
382 }
383
384 /**
385 * Returns true if there are no key-value mappings currently in this Map
386 * @return <code>size() == 0</code>
387 */
388 public boolean isEmpty()
389 {
390 return size == 0;
391 }
392
393 /**
394 * Returns a "set view" of this Map's keys. The set is backed by the
395 * Map, so changes in one show up in the other. The set supports
396 * element removal, but not element addition.
397 * <p>
398 *
399 * <em>The semantics of this set are different from the contract of Set
400 * in order to make IdentityHashMap work. This means that while you can
401 * compare these objects between IdentityHashMaps, comparing them with
402 * regular sets is likely to have undefined behavior.</em> The hashCode
403 * of the set is the sum of the identity hash codes, instead of the
404 * regular hashCodes, and equality is determined by reference instead
405 * of by the equals method.
406 * <p>
407 *
408 * @return a set view of the keys
409 * @see #values()
410 * @see #entrySet()
411 */
412 public Set keySet()
413 {
414 if (keys == null)
415 keys = new AbstractSet()
416 {
417 public int size()
418 {
419 return size;
420 }
421
422 public Iterator iterator()
423 {
424 return new IdentityIterator(KEYS);
425 }
426
427 public void clear()
428 {
429 IdentityHashMap.this.clear();
430 }
431
432 public boolean contains(Object o)
433 {
434 return containsKey(o);
435 }
436
437 public int hashCode()
438 {
439 int hash = 0;
440 for (int i = table.length - 2; i >= 0; i -= 2)
441 {
442 Object key = table[i];
443 if (key == emptyslot || key == tombstone)
444 continue;
445 hash += System.identityHashCode(key);
446 }
447 return hash;
448
449 }
450
451 public boolean remove(Object o)
452 {
453 int h = hash(o);
454 if (table[h] == o)
455 {
456 size--;
457 modCount++;
458 table[h] = tombstone;
459 table[h + 1] = tombstone;
460 return true;
461 }
462 return false;
463 }
464 };
465 return keys;
466 }
467
468 /**
469 * Puts the supplied value into the Map, mapped by the supplied key.
470 * The value may be retrieved by any object which <code>equals()</code>
471 * this key. NOTE: Since the prior value could also be null, you must
472 * first use containsKey if you want to see if you are replacing the
473 * key's mapping. Unlike normal maps, this tests for the key
474 * with <code>entry == key</code> instead of
475 * <code>entry == null ? key == null : entry.equals(key)</code>.
476 *
477 * @param key the key used to locate the value
478 * @param value the value to be stored in the HashMap
479 * @return the prior mapping of the key, or null if there was none
480 * @see #get(Object)
481 */
482 public Object put(Object key, Object value)
483 {
484 // Rehash if the load factor is too high.
485 if (size > threshold)
486 {
487 Object[] old = table;
488 // This isn't necessarily prime, but it is an odd number of key/value
489 // slots, which has a higher probability of fewer collisions.
490 table = new Object[old.length * 2 + 2];
491 Arrays.fill(table, emptyslot);
492 size = 0;
493 threshold = (table.length / 2) / 4 * 3;
494
495 for (int i = old.length - 2; i >= 0; i -= 2)
496 {
497 Object oldkey = old[i];
498 if (oldkey != tombstone && oldkey != emptyslot)
499 // Just use put. This isn't very efficient, but it is ok.
500 put(oldkey, old[i + 1]);
501 }
502 }
503
504 int h = hash(key);
505 if (table[h] == key)
506 {
507 Object r = table[h + 1];
508 table[h + 1] = value;
509 return r;
510 }
511
512 // At this point, we add a new mapping.
513 modCount++;
514 size++;
515 table[h] = key;
516 table[h + 1] = value;
517 return null;
518 }
519
520 /**
521 * Copies all of the mappings from the specified map to this. If a key
522 * is already in this map, its value is replaced.
523 *
524 * @param m the map to copy
525 * @throws NullPointerException if m is null
526 */
527 public void putAll(Map m)
528 {
529 // Why did Sun specify this one? The superclass does the right thing.
530 super.putAll(m);
531 }
532
533 /**
534 * Removes from the HashMap and returns the value which is mapped by the
535 * supplied key. If the key maps to nothing, then the HashMap remains
536 * unchanged, and <pre>null</pre> is returned. NOTE: Since the value
537 * could also be null, you must use containsKey to see if you are
538 * actually removing a mapping. Unlike normal maps, this tests for the
539 * key with <code>entry == key</code> instead of
540 * <code>entry == null ? key == null : entry.equals(key)</code>.
541 *
542 * @param key the key used to locate the value to remove
543 * @return whatever the key mapped to, if present
544 */
545 public Object remove(Object key)
546 {
547 int h = hash(key);
548 if (table[h] == key)
549 {
550 modCount++;
551 size--;
552 Object r = table[h + 1];
553 table[h] = tombstone;
554 table[h + 1] = tombstone;
555 return r;
556 }
557 return null;
558 }
559
560 /**
561 * Returns the number of kay-value mappings currently in this Map
562 * @return the size
563 */
564 public int size()
565 {
566 return size;
567 }
568
569 /**
570 * Returns a "collection view" (or "bag view") of this Map's values.
571 * The collection is backed by the Map, so changes in one show up
572 * in the other. The collection supports element removal, but not element
573 * addition.
574 * <p>
575 *
576 * <em>The semantics of this set are different from the contract of
577 * Collection in order to make IdentityHashMap work. This means that
578 * while you can compare these objects between IdentityHashMaps, comparing
579 * them with regular sets is likely to have undefined behavior.</em>
580 * Likewise, contains and remove go by == instead of equals().
581 * <p>
582 *
583 * @return a bag view of the values
584 * @see #keySet()
585 * @see #entrySet()
586 */
587 public Collection values()
588 {
589 if (values == null)
590 values = new AbstractCollection()
591 {
592 public int size()
593 {
594 return size;
595 }
596
597 public Iterator iterator()
598 {
599 return new IdentityIterator(VALUES);
600 }
601
602 public void clear()
603 {
604 IdentityHashMap.this.clear();
605 }
606
607 public boolean remove(Object o)
608 {
609 for (int i = table.length - 1; i > 0; i -= 2)
610 if (table[i] == o)
611 {
612 modCount++;
613 table[i - 1] = tombstone;
614 table[i] = tombstone;
615 size--;
616 return true;
617 }
618 return false;
619 }
620 };
621 return values;
622 }
623
624 /**
625 * Helper method which computes the hash code, then traverses the table
626 * until it finds the key, or the spot where the key would go.
627 *
628 * @param key the key to check
629 * @return the index where the key belongs
630 * @see #IdentityHashMap(int)
631 * @see #put(Object, Object)
632 */
633 // Package visible for use by nested classes.
634 int hash(Object key)
635 {
636 // Implementation note: it is feasible for the table to have no
637 // emptyslots, if it is full with entries and tombstones, so we must
638 // remember where we started. If we encounter the key or an emptyslot,
639 // we are done. If we encounter a tombstone, the key may still be in
640 // the array. If we don't encounter the key, we use the first emptyslot
641 // or tombstone we encountered as the location where the key would go.
642 // By requiring at least 2 key/value slots, and rehashing at 75%
643 // capacity, we guarantee that there will always be either an emptyslot
644 // or a tombstone somewhere in the table.
645 int h = 2 * Math.abs(System.identityHashCode(key) % (table.length / 2));
646 int del = -1;
647 int save = h;
648
649 do
650 {
651 if (table[h] == key)
652 return h;
653 if (table[h] == emptyslot)
654 break;
655 if (table[h] == tombstone && del < 0)
656 del = h;
657 h -= 2;
658 if (h < 0)
659 h = table.length - 2;
660 }
661 while (h != save);
662
663 return del < 0 ? h : del;
664 }
665
666 /**
667 * This class allows parameterized iteration over IdentityHashMaps. Based
668 * on its construction, it returns the key or value of a mapping, or
669 * creates the appropriate Map.Entry object with the correct fail-fast
670 * semantics and identity comparisons.
671 *
672 * @author Tom Tromey <[email protected]>
673 * @author Eric Blake <[email protected]>
674 */
675 private final class IdentityIterator implements Iterator
676 {
677 /**
678 * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
679 * or {@link #ENTRIES}.
680 */
681 final int type;
682 /** The number of modifications to the backing Map that we know about. */
683 int knownMod = modCount;
684 /** The number of elements remaining to be returned by next(). */
685 int count = size;
686 /** Location in the table. */
687 int loc = table.length;
688
689 /**
690 * Construct a new Iterator with the supplied type.
691 * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
692 */
693 IdentityIterator(int type)
694 {
695 this.type = type;
696 }
697
698 /**
699 * Returns true if the Iterator has more elements.
700 * @return true if there are more elements
701 * @throws ConcurrentModificationException if the Map was modified
702 */
703 public boolean hasNext()
704 {
705 if (knownMod != modCount)
706 throw new ConcurrentModificationException();
707 return count > 0;
708 }
709
710 /**
711 * Returns the next element in the Iterator's sequential view.
712 * @return the next element
713 * @throws ConcurrentModificationException if the Map was modified
714 * @throws NoSuchElementException if there is none
715 */
716 public Object next()
717 {
718 if (knownMod != modCount)
719 throw new ConcurrentModificationException();
720 if (count == 0)
721 throw new NoSuchElementException();
722 count--;
723
724 Object key;
725 do
726 {
727 loc -= 2;
728 key = table[loc];
729 }
730 while (key == emptyslot || key == tombstone);
731
732 return type == KEYS ? key : (type == VALUES ? table[loc + 1]
733 : new IdentityEntry(loc));
734 }
735
736 /**
737 * Removes from the backing Map the last element which was fetched
738 * with the <pre>next()</pre> method.
739 * @throws ConcurrentModificationException if the Map was modified
740 * @throws IllegalStateException if called when there is no last element
741 */
742 public void remove()
743 {
744 if (knownMod != modCount)
745 throw new ConcurrentModificationException();
746 if (loc == table.length || table[loc] == tombstone)
747 throw new IllegalStateException();
748 modCount++;
749 size--;
750 table[loc] = tombstone;
751 table[loc + 1] = tombstone;
752 knownMod++;
753 }
754 } // class IdentityIterator
755
756 /**
757 * This class provides Map.Entry objects for IdentityHashMaps. The entry
758 * is fail-fast, and will throw a ConcurrentModificationException if
759 * the underlying map is modified, or if remove is called on the iterator
760 * that generated this object. It is identity based, so it violates
761 * the general contract of Map.Entry, and is probably unsuitable for
762 * comparison to normal maps; but it works among other IdentityHashMaps.
763 *
764 * @author Eric Blake <[email protected]>
765 */
766 private final class IdentityEntry implements Map.Entry
767 {
768 /** The location of this entry. */
769 final int loc;
770 /** The number of modifications to the backing Map that we know about. */
771 final int knownMod = modCount;
772
773 /**
774 * Constructs the Entry.
775 *
776 * @param loc the location of this entry in table
777 */
778 IdentityEntry(int loc)
779 {
780 this.loc = loc;
781 }
782
783 /**
784 * Compares the specified object with this entry, using identity
785 * semantics. Note that this can lead to undefined results with
786 * Entry objects created by normal maps.
787 *
788 * @param o the object to compare
789 * @return true if it is equal
790 * @throws ConcurrentModificationException if the entry was invalidated
791 * by modifying the Map or calling Iterator.remove()
792 */
793 public boolean equals(Object o)
794 {
795 if (knownMod != modCount || table[loc] == tombstone)
796 throw new ConcurrentModificationException();
797 if (! (o instanceof Map.Entry))
798 return false;
799 Map.Entry e = (Map.Entry) o;
800 return table[loc] == e.getKey() && table[loc + 1] == e.getValue();
801 }
802
803 /**
804 * Returns the key of this entry.
805 *
806 * @return the key
807 * @throws ConcurrentModificationException if the entry was invalidated
808 * by modifying the Map or calling Iterator.remove()
809 */
810 public Object getKey()
811 {
812 if (knownMod != modCount || table[loc] == tombstone)
813 throw new ConcurrentModificationException();
814 return table[loc];
815 }
816
817 /**
818 * Returns the value of this entry.
819 *
820 * @return the value
821 * @throws ConcurrentModificationException if the entry was invalidated
822 * by modifying the Map or calling Iterator.remove()
823 */
824 public Object getValue()
825 {
826 if (knownMod != modCount || table[loc] == tombstone)
827 throw new ConcurrentModificationException();
828 return table[loc + 1];
829 }
830
831 /**
832 * Returns the hashcode of the entry, using identity semantics.
833 * Note that this can lead to undefined results with Entry objects
834 * created by normal maps.
835 *
836 * @return the hash code
837 * @throws ConcurrentModificationException if the entry was invalidated
838 * by modifying the Map or calling Iterator.remove()
839 */
840 public int hashCode()
841 {
842 if (knownMod != modCount || table[loc] == tombstone)
843 throw new ConcurrentModificationException();
844 return (System.identityHashCode(table[loc])
845 ^ System.identityHashCode(table[loc + 1]));
846 }
847
848 /**
849 * Replaces the value of this mapping, and returns the old value.
850 *
851 * @param value the new value
852 * @return the old value
853 * @throws ConcurrentModificationException if the entry was invalidated
854 * by modifying the Map or calling Iterator.remove()
855 */
856 public Object setValue(Object value)
857 {
858 if (knownMod != modCount || table[loc] == tombstone)
859 throw new ConcurrentModificationException();
860 Object r = table[loc + 1];
861 table[loc + 1] = value;
862 return r;
863 }
864
865 /**
866 * This provides a string representation of the entry. It is of the form
867 * "key=value", where string concatenation is used on key and value.
868 *
869 * @return the string representation
870 * @throws ConcurrentModificationException if the entry was invalidated
871 * by modifying the Map or calling Iterator.remove()
872 */
873 public final String toString()
874 {
875 if (knownMod != modCount || table[loc] == tombstone)
876 throw new ConcurrentModificationException();
877 return table[loc] + "=" + table[loc + 1];
878 }
879 } // class IdentityEntry
880
881 /**
882 * Reads the object from a serial stream.
883 *
884 * @param s the stream to read from
885 * @throws ClassNotFoundException if the underlying stream fails
886 * @throws IOException if the underlying stream fails
887 * @serialData expects the size (int), followed by that many key (Object)
888 * and value (Object) pairs, with the pairs in no particular
889 * order
890 */
891 private void readObject(ObjectInputStream s)
892 throws IOException, ClassNotFoundException
893 {
894 s.defaultReadObject();
895
896 int num = s.readInt();
897 table = new Object[2 * Math.max(num * 2, DEFAULT_CAPACITY)];
898 // Read key/value pairs.
899 while (--num >= 0)
900 put(s.readObject(), s.readObject());
901 }
902
903 /**
904 * Writes the object to a serial stream.
905 *
906 * @param s the stream to write to
907 * @throws IOException if the underlying stream fails
908 * @serialData outputs the size (int), followed by that many key (Object)
909 * and value (Object) pairs, with the pairs in no particular
910 * order
911 */
912 private void writeObject(ObjectOutputStream s)
913 throws IOException
914 {
915 s.defaultWriteObject();
916 s.writeInt(size);
917 for (int i = table.length - 2; i >= 0; i -= 2)
918 {
919 Object key = table[i];
920 if (key != tombstone && key != emptyslot)
921 {
922 s.writeObject(key);
923 s.writeObject(table[i + 1]);
924 }
925 }
926 }
927}
Note: See TracBrowser for help on using the repository browser.