310 return (i + 2 < len ? i + 2 : 0);
311 }
312
313 /**
314 * Returns the value to which the specified key is mapped,
315 * or {@code null} if this map contains no mapping for the key.
316 *
317 * <p>More formally, if this map contains a mapping from a key
318 * {@code k} to a value {@code v} such that {@code (key == k)},
319 * then this method returns {@code v}; otherwise it returns
320 * {@code null}. (There can be at most one such mapping.)
321 *
322 * <p>A return value of {@code null} does not <i>necessarily</i>
323 * indicate that the map contains no mapping for the key; it's also
324 * possible that the map explicitly maps the key to {@code null}.
325 * The {@link #containsKey containsKey} operation may be used to
326 * distinguish these two cases.
327 *
328 * @see #put(Object, Object)
329 */
330 public V get(Object key) {
331 Object k = maskNull(key);
332 Object[] tab = table;
333 int len = tab.length;
334 int i = hash(k, len);
335 while (true) {
336 Object item = tab[i];
337 if (item == k)
338 return (V) tab[i + 1];
339 if (item == null)
340 return null;
341 i = nextKeyIndex(i, len);
342 }
343 }
344
345 /**
346 * Tests whether the specified object reference is a key in this identity
347 * hash map.
348 *
349 * @param key possible key
414 *
415 * @param key the key with which the specified value is to be associated
416 * @param value the value to be associated with the specified key
417 * @return the previous value associated with <tt>key</tt>, or
418 * <tt>null</tt> if there was no mapping for <tt>key</tt>.
419 * (A <tt>null</tt> return can also indicate that the map
420 * previously associated <tt>null</tt> with <tt>key</tt>.)
421 * @see Object#equals(Object)
422 * @see #get(Object)
423 * @see #containsKey(Object)
424 */
425 public V put(K key, V value) {
426 Object k = maskNull(key);
427 Object[] tab = table;
428 int len = tab.length;
429 int i = hash(k, len);
430
431 Object item;
432 while ( (item = tab[i]) != null) {
433 if (item == k) {
434 V oldValue = (V) tab[i + 1];
435 tab[i + 1] = value;
436 return oldValue;
437 }
438 i = nextKeyIndex(i, len);
439 }
440
441 modCount++;
442 tab[i] = k;
443 tab[i + 1] = value;
444 if (++size >= threshold)
445 resize(len); // len == 2 * current capacity.
446 return null;
447 }
448
449 /**
450 * Resize the table to hold given capacity.
451 *
452 * @param newCapacity the new capacity, must be a power of two.
453 */
507 /**
508 * Removes the mapping for this key from this map if present.
509 *
510 * @param key key whose mapping is to be removed from the map
511 * @return the previous value associated with <tt>key</tt>, or
512 * <tt>null</tt> if there was no mapping for <tt>key</tt>.
513 * (A <tt>null</tt> return can also indicate that the map
514 * previously associated <tt>null</tt> with <tt>key</tt>.)
515 */
516 public V remove(Object key) {
517 Object k = maskNull(key);
518 Object[] tab = table;
519 int len = tab.length;
520 int i = hash(k, len);
521
522 while (true) {
523 Object item = tab[i];
524 if (item == k) {
525 modCount++;
526 size--;
527 V oldValue = (V) tab[i + 1];
528 tab[i + 1] = null;
529 tab[i] = null;
530 closeDeletion(i);
531 return oldValue;
532 }
533 if (item == null)
534 return null;
535 i = nextKeyIndex(i, len);
536 }
537
538 }
539
540 /**
541 * Removes the specified key-value mapping from the map if it is present.
542 *
543 * @param key possible key
544 * @param value possible value
545 * @return <code>true</code> if and only if the specified key-value
546 * mapping was in the map
621 * Compares the specified object with this map for equality. Returns
622 * <tt>true</tt> if the given object is also a map and the two maps
623 * represent identical object-reference mappings. More formally, this
624 * map is equal to another map <tt>m</tt> if and only if
625 * <tt>this.entrySet().equals(m.entrySet())</tt>.
626 *
627 * <p><b>Owing to the reference-equality-based semantics of this map it is
628 * possible that the symmetry and transitivity requirements of the
629 * <tt>Object.equals</tt> contract may be violated if this map is compared
630 * to a normal map. However, the <tt>Object.equals</tt> contract is
631 * guaranteed to hold among <tt>IdentityHashMap</tt> instances.</b>
632 *
633 * @param o object to be compared for equality with this map
634 * @return <tt>true</tt> if the specified object is equal to this map
635 * @see Object#equals(Object)
636 */
637 public boolean equals(Object o) {
638 if (o == this) {
639 return true;
640 } else if (o instanceof IdentityHashMap) {
641 IdentityHashMap m = (IdentityHashMap) o;
642 if (m.size() != size)
643 return false;
644
645 Object[] tab = m.table;
646 for (int i = 0; i < tab.length; i+=2) {
647 Object k = tab[i];
648 if (k != null && !containsMapping(k, tab[i + 1]))
649 return false;
650 }
651 return true;
652 } else if (o instanceof Map) {
653 Map m = (Map)o;
654 return entrySet().equals(m.entrySet());
655 } else {
656 return false; // o is not a Map
657 }
658 }
659
660 /**
661 * Returns the hash code value for this map. The hash code of a map is
662 * defined to be the sum of the hash codes of each entry in the map's
663 * <tt>entrySet()</tt> view. This ensures that <tt>m1.equals(m2)</tt>
664 * implies that <tt>m1.hashCode()==m2.hashCode()</tt> for any two
665 * <tt>IdentityHashMap</tt> instances <tt>m1</tt> and <tt>m2</tt>, as
666 * required by the general contract of {@link Object#hashCode}.
667 *
668 * <p><b>Owing to the reference-equality-based semantics of the
669 * <tt>Map.Entry</tt> instances in the set returned by this map's
670 * <tt>entrySet</tt> method, it is possible that the contractual
671 * requirement of <tt>Object.hashCode</tt> mentioned in the previous
672 * paragraph will be violated if one of the two objects being compared is
673 * an <tt>IdentityHashMap</tt> instance and the other is a normal map.</b>
681 Object[] tab = table;
682 for (int i = 0; i < tab.length; i +=2) {
683 Object key = tab[i];
684 if (key != null) {
685 Object k = unmaskNull(key);
686 result += System.identityHashCode(k) ^
687 System.identityHashCode(tab[i + 1]);
688 }
689 }
690 return result;
691 }
692
693 /**
694 * Returns a shallow copy of this identity hash map: the keys and values
695 * themselves are not cloned.
696 *
697 * @return a shallow copy of this map
698 */
699 public Object clone() {
700 try {
701 IdentityHashMap<K,V> m = (IdentityHashMap<K,V>) super.clone();
702 m.entrySet = null;
703 m.table = table.clone();
704 return m;
705 } catch (CloneNotSupportedException e) {
706 throw new InternalError(e);
707 }
708 }
709
710 private abstract class IdentityHashMapIterator<T> implements Iterator<T> {
711 int index = (size != 0 ? 0 : table.length); // current slot.
712 int expectedModCount = modCount; // to support fast-fail
713 int lastReturnedIndex = -1; // to allow remove()
714 boolean indexValid; // To avoid unnecessary next computation
715 Object[] traversalTable = table; // reference to main table or copy
716
717 public boolean hasNext() {
718 Object[] tab = traversalTable;
719 for (int i = index; i < tab.length; i+=2) {
720 Object key = tab[i];
721 if (key != null) {
751 // back up index to revisit new contents after deletion
752 index = deletedSlot;
753 indexValid = false;
754
755 // Removal code proceeds as in closeDeletion except that
756 // it must catch the rare case where an element already
757 // seen is swapped into a vacant slot that will be later
758 // traversed by this iterator. We cannot allow future
759 // next() calls to return it again. The likelihood of
760 // this occurring under 2/3 load factor is very slim, but
761 // when it does happen, we must make a copy of the rest of
762 // the table to use for the rest of the traversal. Since
763 // this can only happen when we are near the end of the table,
764 // even in these rare cases, this is not very expensive in
765 // time or space.
766
767 Object[] tab = traversalTable;
768 int len = tab.length;
769
770 int d = deletedSlot;
771 K key = (K) tab[d];
772 tab[d] = null; // vacate the slot
773 tab[d + 1] = null;
774
775 // If traversing a copy, remove in real table.
776 // We can skip gap-closure on copy.
777 if (tab != IdentityHashMap.this.table) {
778 IdentityHashMap.this.remove(key);
779 expectedModCount = modCount;
780 return;
781 }
782
783 size--;
784
785 Object item;
786 for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
787 i = nextKeyIndex(i, len)) {
788 int r = hash(item, len);
789 // See closeDeletion for explanation of this conditional
790 if ((i < r && (r <= d || d <= i)) ||
791 (r <= d && d <= i)) {
801 traversalTable == IdentityHashMap.this.table) {
802 int remaining = len - deletedSlot;
803 Object[] newTable = new Object[remaining];
804 System.arraycopy(tab, deletedSlot,
805 newTable, 0, remaining);
806 traversalTable = newTable;
807 index = 0;
808 }
809
810 tab[d] = item;
811 tab[d + 1] = tab[i + 1];
812 tab[i] = null;
813 tab[i + 1] = null;
814 d = i;
815 }
816 }
817 }
818 }
819
820 private class KeyIterator extends IdentityHashMapIterator<K> {
821 public K next() {
822 return (K) unmaskNull(traversalTable[nextIndex()]);
823 }
824 }
825
826 private class ValueIterator extends IdentityHashMapIterator<V> {
827 public V next() {
828 return (V) traversalTable[nextIndex() + 1];
829 }
830 }
831
832 private class EntryIterator
833 extends IdentityHashMapIterator<Map.Entry<K,V>>
834 {
835 private Entry lastReturnedEntry = null;
836
837 public Map.Entry<K,V> next() {
838 lastReturnedEntry = new Entry(nextIndex());
839 return lastReturnedEntry;
840 }
841
842 public void remove() {
843 lastReturnedIndex =
844 ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
845 super.remove();
846 lastReturnedEntry.index = lastReturnedIndex;
847 lastReturnedEntry = null;
848 }
849
850 private class Entry implements Map.Entry<K,V> {
851 private int index;
852
853 private Entry(int index) {
854 this.index = index;
855 }
856
857 public K getKey() {
858 checkIndexForEntryUse();
859 return (K) unmaskNull(traversalTable[index]);
860 }
861
862 public V getValue() {
863 checkIndexForEntryUse();
864 return (V) traversalTable[index+1];
865 }
866
867 public V setValue(V value) {
868 checkIndexForEntryUse();
869 V oldValue = (V) traversalTable[index+1];
870 traversalTable[index+1] = value;
871 // if shadowing, force into main table
872 if (traversalTable != IdentityHashMap.this.table)
873 put((K) traversalTable[index], value);
874 return oldValue;
875 }
876
877 public boolean equals(Object o) {
878 if (index < 0)
879 return super.equals(o);
880
881 if (!(o instanceof Map.Entry))
882 return false;
883 Map.Entry e = (Map.Entry)o;
884 return (e.getKey() == unmaskNull(traversalTable[index]) &&
885 e.getValue() == traversalTable[index+1]);
886 }
887
888 public int hashCode() {
889 if (lastReturnedIndex < 0)
890 return super.hashCode();
891
892 return (System.identityHashCode(unmaskNull(traversalTable[index])) ^
893 System.identityHashCode(traversalTable[index+1]));
894 }
895
896 public String toString() {
897 if (index < 0)
898 return super.toString();
899
900 return (unmaskNull(traversalTable[index]) + "="
901 + traversalTable[index+1]);
902 }
903
1092 * hold among identity-based map entries, and among sets of such entries.
1093 * </b>
1094 *
1095 * @return a set view of the identity-mappings contained in this map
1096 */
1097 public Set<Map.Entry<K,V>> entrySet() {
1098 Set<Map.Entry<K,V>> es = entrySet;
1099 if (es != null)
1100 return es;
1101 else
1102 return entrySet = new EntrySet();
1103 }
1104
1105 private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
1106 public Iterator<Map.Entry<K,V>> iterator() {
1107 return new EntryIterator();
1108 }
1109 public boolean contains(Object o) {
1110 if (!(o instanceof Map.Entry))
1111 return false;
1112 Map.Entry entry = (Map.Entry)o;
1113 return containsMapping(entry.getKey(), entry.getValue());
1114 }
1115 public boolean remove(Object o) {
1116 if (!(o instanceof Map.Entry))
1117 return false;
1118 Map.Entry entry = (Map.Entry)o;
1119 return removeMapping(entry.getKey(), entry.getValue());
1120 }
1121 public int size() {
1122 return size;
1123 }
1124 public void clear() {
1125 IdentityHashMap.this.clear();
1126 }
1127 /*
1128 * Must revert from AbstractSet's impl to AbstractCollection's, as
1129 * the former contains an optimization that results in incorrect
1130 * behavior when c is a smaller "normal" (non-identity-based) Set.
1131 */
1132 public boolean removeAll(Collection<?> c) {
1133 boolean modified = false;
1134 for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) {
1135 if (c.contains(i.next())) {
1136 i.remove();
1137 modified = true;
1138 }
1196 }
1197 }
1198
1199 /**
1200 * Reconstitute the <tt>IdentityHashMap</tt> instance from a stream (i.e.,
1201 * deserialize it).
1202 */
1203 private void readObject(java.io.ObjectInputStream s)
1204 throws java.io.IOException, ClassNotFoundException {
1205 // Read in any hidden stuff
1206 s.defaultReadObject();
1207
1208 // Read in size (number of Mappings)
1209 int size = s.readInt();
1210
1211 // Allow for 33% growth (i.e., capacity is >= 2* size()).
1212 init(capacity((size*4)/3));
1213
1214 // Read the keys and values, and put the mappings in the table
1215 for (int i=0; i<size; i++) {
1216 K key = (K) s.readObject();
1217 V value = (V) s.readObject();
1218 putForCreate(key, value);
1219 }
1220 }
1221
1222 /**
1223 * The put method for readObject. It does not resize the table,
1224 * update modCount, etc.
1225 */
1226 private void putForCreate(K key, V value)
1227 throws IOException
1228 {
1229 K k = (K)maskNull(key);
1230 Object[] tab = table;
1231 int len = tab.length;
1232 int i = hash(k, len);
1233
1234 Object item;
1235 while ( (item = tab[i]) != null) {
1236 if (item == k)
1237 throw new java.io.StreamCorruptedException();
1238 i = nextKeyIndex(i, len);
1239 }
1240 tab[i] = k;
1241 tab[i + 1] = value;
1242 }
1243 }
|
310 return (i + 2 < len ? i + 2 : 0);
311 }
312
313 /**
314 * Returns the value to which the specified key is mapped,
315 * or {@code null} if this map contains no mapping for the key.
316 *
317 * <p>More formally, if this map contains a mapping from a key
318 * {@code k} to a value {@code v} such that {@code (key == k)},
319 * then this method returns {@code v}; otherwise it returns
320 * {@code null}. (There can be at most one such mapping.)
321 *
322 * <p>A return value of {@code null} does not <i>necessarily</i>
323 * indicate that the map contains no mapping for the key; it's also
324 * possible that the map explicitly maps the key to {@code null}.
325 * The {@link #containsKey containsKey} operation may be used to
326 * distinguish these two cases.
327 *
328 * @see #put(Object, Object)
329 */
330 @SuppressWarnings("unchecked")
331 public V get(Object key) {
332 Object k = maskNull(key);
333 Object[] tab = table;
334 int len = tab.length;
335 int i = hash(k, len);
336 while (true) {
337 Object item = tab[i];
338 if (item == k)
339 return (V) tab[i + 1];
340 if (item == null)
341 return null;
342 i = nextKeyIndex(i, len);
343 }
344 }
345
346 /**
347 * Tests whether the specified object reference is a key in this identity
348 * hash map.
349 *
350 * @param key possible key
415 *
416 * @param key the key with which the specified value is to be associated
417 * @param value the value to be associated with the specified key
418 * @return the previous value associated with <tt>key</tt>, or
419 * <tt>null</tt> if there was no mapping for <tt>key</tt>.
420 * (A <tt>null</tt> return can also indicate that the map
421 * previously associated <tt>null</tt> with <tt>key</tt>.)
422 * @see Object#equals(Object)
423 * @see #get(Object)
424 * @see #containsKey(Object)
425 */
426 public V put(K key, V value) {
427 Object k = maskNull(key);
428 Object[] tab = table;
429 int len = tab.length;
430 int i = hash(k, len);
431
432 Object item;
433 while ( (item = tab[i]) != null) {
434 if (item == k) {
435 @SuppressWarnings("unchecked")
436 V oldValue = (V) tab[i + 1];
437 tab[i + 1] = value;
438 return oldValue;
439 }
440 i = nextKeyIndex(i, len);
441 }
442
443 modCount++;
444 tab[i] = k;
445 tab[i + 1] = value;
446 if (++size >= threshold)
447 resize(len); // len == 2 * current capacity.
448 return null;
449 }
450
451 /**
452 * Resize the table to hold given capacity.
453 *
454 * @param newCapacity the new capacity, must be a power of two.
455 */
509 /**
510 * Removes the mapping for this key from this map if present.
511 *
512 * @param key key whose mapping is to be removed from the map
513 * @return the previous value associated with <tt>key</tt>, or
514 * <tt>null</tt> if there was no mapping for <tt>key</tt>.
515 * (A <tt>null</tt> return can also indicate that the map
516 * previously associated <tt>null</tt> with <tt>key</tt>.)
517 */
518 public V remove(Object key) {
519 Object k = maskNull(key);
520 Object[] tab = table;
521 int len = tab.length;
522 int i = hash(k, len);
523
524 while (true) {
525 Object item = tab[i];
526 if (item == k) {
527 modCount++;
528 size--;
529 @SuppressWarnings("unchecked")
530 V oldValue = (V) tab[i + 1];
531 tab[i + 1] = null;
532 tab[i] = null;
533 closeDeletion(i);
534 return oldValue;
535 }
536 if (item == null)
537 return null;
538 i = nextKeyIndex(i, len);
539 }
540
541 }
542
543 /**
544 * Removes the specified key-value mapping from the map if it is present.
545 *
546 * @param key possible key
547 * @param value possible value
548 * @return <code>true</code> if and only if the specified key-value
549 * mapping was in the map
624 * Compares the specified object with this map for equality. Returns
625 * <tt>true</tt> if the given object is also a map and the two maps
626 * represent identical object-reference mappings. More formally, this
627 * map is equal to another map <tt>m</tt> if and only if
628 * <tt>this.entrySet().equals(m.entrySet())</tt>.
629 *
630 * <p><b>Owing to the reference-equality-based semantics of this map it is
631 * possible that the symmetry and transitivity requirements of the
632 * <tt>Object.equals</tt> contract may be violated if this map is compared
633 * to a normal map. However, the <tt>Object.equals</tt> contract is
634 * guaranteed to hold among <tt>IdentityHashMap</tt> instances.</b>
635 *
636 * @param o object to be compared for equality with this map
637 * @return <tt>true</tt> if the specified object is equal to this map
638 * @see Object#equals(Object)
639 */
640 public boolean equals(Object o) {
641 if (o == this) {
642 return true;
643 } else if (o instanceof IdentityHashMap) {
644 IdentityHashMap<?,?> m = (IdentityHashMap<?,?>) o;
645 if (m.size() != size)
646 return false;
647
648 Object[] tab = m.table;
649 for (int i = 0; i < tab.length; i+=2) {
650 Object k = tab[i];
651 if (k != null && !containsMapping(k, tab[i + 1]))
652 return false;
653 }
654 return true;
655 } else if (o instanceof Map) {
656 Map<?,?> m = (Map<?,?>)o;
657 return entrySet().equals(m.entrySet());
658 } else {
659 return false; // o is not a Map
660 }
661 }
662
663 /**
664 * Returns the hash code value for this map. The hash code of a map is
665 * defined to be the sum of the hash codes of each entry in the map's
666 * <tt>entrySet()</tt> view. This ensures that <tt>m1.equals(m2)</tt>
667 * implies that <tt>m1.hashCode()==m2.hashCode()</tt> for any two
668 * <tt>IdentityHashMap</tt> instances <tt>m1</tt> and <tt>m2</tt>, as
669 * required by the general contract of {@link Object#hashCode}.
670 *
671 * <p><b>Owing to the reference-equality-based semantics of the
672 * <tt>Map.Entry</tt> instances in the set returned by this map's
673 * <tt>entrySet</tt> method, it is possible that the contractual
674 * requirement of <tt>Object.hashCode</tt> mentioned in the previous
675 * paragraph will be violated if one of the two objects being compared is
676 * an <tt>IdentityHashMap</tt> instance and the other is a normal map.</b>
684 Object[] tab = table;
685 for (int i = 0; i < tab.length; i +=2) {
686 Object key = tab[i];
687 if (key != null) {
688 Object k = unmaskNull(key);
689 result += System.identityHashCode(k) ^
690 System.identityHashCode(tab[i + 1]);
691 }
692 }
693 return result;
694 }
695
696 /**
697 * Returns a shallow copy of this identity hash map: the keys and values
698 * themselves are not cloned.
699 *
700 * @return a shallow copy of this map
701 */
702 public Object clone() {
703 try {
704 IdentityHashMap<?,?> m = (IdentityHashMap<?,?>) super.clone();
705 m.entrySet = null;
706 m.table = table.clone();
707 return m;
708 } catch (CloneNotSupportedException e) {
709 throw new InternalError(e);
710 }
711 }
712
713 private abstract class IdentityHashMapIterator<T> implements Iterator<T> {
714 int index = (size != 0 ? 0 : table.length); // current slot.
715 int expectedModCount = modCount; // to support fast-fail
716 int lastReturnedIndex = -1; // to allow remove()
717 boolean indexValid; // To avoid unnecessary next computation
718 Object[] traversalTable = table; // reference to main table or copy
719
720 public boolean hasNext() {
721 Object[] tab = traversalTable;
722 for (int i = index; i < tab.length; i+=2) {
723 Object key = tab[i];
724 if (key != null) {
754 // back up index to revisit new contents after deletion
755 index = deletedSlot;
756 indexValid = false;
757
758 // Removal code proceeds as in closeDeletion except that
759 // it must catch the rare case where an element already
760 // seen is swapped into a vacant slot that will be later
761 // traversed by this iterator. We cannot allow future
762 // next() calls to return it again. The likelihood of
763 // this occurring under 2/3 load factor is very slim, but
764 // when it does happen, we must make a copy of the rest of
765 // the table to use for the rest of the traversal. Since
766 // this can only happen when we are near the end of the table,
767 // even in these rare cases, this is not very expensive in
768 // time or space.
769
770 Object[] tab = traversalTable;
771 int len = tab.length;
772
773 int d = deletedSlot;
774 Object key = tab[d];
775 tab[d] = null; // vacate the slot
776 tab[d + 1] = null;
777
778 // If traversing a copy, remove in real table.
779 // We can skip gap-closure on copy.
780 if (tab != IdentityHashMap.this.table) {
781 IdentityHashMap.this.remove(key);
782 expectedModCount = modCount;
783 return;
784 }
785
786 size--;
787
788 Object item;
789 for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
790 i = nextKeyIndex(i, len)) {
791 int r = hash(item, len);
792 // See closeDeletion for explanation of this conditional
793 if ((i < r && (r <= d || d <= i)) ||
794 (r <= d && d <= i)) {
804 traversalTable == IdentityHashMap.this.table) {
805 int remaining = len - deletedSlot;
806 Object[] newTable = new Object[remaining];
807 System.arraycopy(tab, deletedSlot,
808 newTable, 0, remaining);
809 traversalTable = newTable;
810 index = 0;
811 }
812
813 tab[d] = item;
814 tab[d + 1] = tab[i + 1];
815 tab[i] = null;
816 tab[i + 1] = null;
817 d = i;
818 }
819 }
820 }
821 }
822
823 private class KeyIterator extends IdentityHashMapIterator<K> {
824 @SuppressWarnings("unchecked")
825 public K next() {
826 return (K) unmaskNull(traversalTable[nextIndex()]);
827 }
828 }
829
830 private class ValueIterator extends IdentityHashMapIterator<V> {
831 @SuppressWarnings("unchecked")
832 public V next() {
833 return (V) traversalTable[nextIndex() + 1];
834 }
835 }
836
837 private class EntryIterator
838 extends IdentityHashMapIterator<Map.Entry<K,V>>
839 {
840 private Entry lastReturnedEntry = null;
841
842 public Map.Entry<K,V> next() {
843 lastReturnedEntry = new Entry(nextIndex());
844 return lastReturnedEntry;
845 }
846
847 public void remove() {
848 lastReturnedIndex =
849 ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
850 super.remove();
851 lastReturnedEntry.index = lastReturnedIndex;
852 lastReturnedEntry = null;
853 }
854
855 private class Entry implements Map.Entry<K,V> {
856 private int index;
857
858 private Entry(int index) {
859 this.index = index;
860 }
861
862 @SuppressWarnings("unchecked")
863 public K getKey() {
864 checkIndexForEntryUse();
865 return (K) unmaskNull(traversalTable[index]);
866 }
867
868 @SuppressWarnings("unchecked")
869 public V getValue() {
870 checkIndexForEntryUse();
871 return (V) traversalTable[index+1];
872 }
873
874 @SuppressWarnings("unchecked")
875 public V setValue(V value) {
876 checkIndexForEntryUse();
877 V oldValue = (V) traversalTable[index+1];
878 traversalTable[index+1] = value;
879 // if shadowing, force into main table
880 if (traversalTable != IdentityHashMap.this.table)
881 put((K) traversalTable[index], value);
882 return oldValue;
883 }
884
885 public boolean equals(Object o) {
886 if (index < 0)
887 return super.equals(o);
888
889 if (!(o instanceof Map.Entry))
890 return false;
891 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
892 return (e.getKey() == unmaskNull(traversalTable[index]) &&
893 e.getValue() == traversalTable[index+1]);
894 }
895
896 public int hashCode() {
897 if (lastReturnedIndex < 0)
898 return super.hashCode();
899
900 return (System.identityHashCode(unmaskNull(traversalTable[index])) ^
901 System.identityHashCode(traversalTable[index+1]));
902 }
903
904 public String toString() {
905 if (index < 0)
906 return super.toString();
907
908 return (unmaskNull(traversalTable[index]) + "="
909 + traversalTable[index+1]);
910 }
911
1100 * hold among identity-based map entries, and among sets of such entries.
1101 * </b>
1102 *
1103 * @return a set view of the identity-mappings contained in this map
1104 */
1105 public Set<Map.Entry<K,V>> entrySet() {
1106 Set<Map.Entry<K,V>> es = entrySet;
1107 if (es != null)
1108 return es;
1109 else
1110 return entrySet = new EntrySet();
1111 }
1112
1113 private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
1114 public Iterator<Map.Entry<K,V>> iterator() {
1115 return new EntryIterator();
1116 }
1117 public boolean contains(Object o) {
1118 if (!(o instanceof Map.Entry))
1119 return false;
1120 Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
1121 return containsMapping(entry.getKey(), entry.getValue());
1122 }
1123 public boolean remove(Object o) {
1124 if (!(o instanceof Map.Entry))
1125 return false;
1126 Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
1127 return removeMapping(entry.getKey(), entry.getValue());
1128 }
1129 public int size() {
1130 return size;
1131 }
1132 public void clear() {
1133 IdentityHashMap.this.clear();
1134 }
1135 /*
1136 * Must revert from AbstractSet's impl to AbstractCollection's, as
1137 * the former contains an optimization that results in incorrect
1138 * behavior when c is a smaller "normal" (non-identity-based) Set.
1139 */
1140 public boolean removeAll(Collection<?> c) {
1141 boolean modified = false;
1142 for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) {
1143 if (c.contains(i.next())) {
1144 i.remove();
1145 modified = true;
1146 }
1204 }
1205 }
1206
1207 /**
1208 * Reconstitute the <tt>IdentityHashMap</tt> instance from a stream (i.e.,
1209 * deserialize it).
1210 */
1211 private void readObject(java.io.ObjectInputStream s)
1212 throws java.io.IOException, ClassNotFoundException {
1213 // Read in any hidden stuff
1214 s.defaultReadObject();
1215
1216 // Read in size (number of Mappings)
1217 int size = s.readInt();
1218
1219 // Allow for 33% growth (i.e., capacity is >= 2* size()).
1220 init(capacity((size*4)/3));
1221
1222 // Read the keys and values, and put the mappings in the table
1223 for (int i=0; i<size; i++) {
1224 @SuppressWarnings("unchecked")
1225 K key = (K) s.readObject();
1226 @SuppressWarnings("unchecked")
1227 V value = (V) s.readObject();
1228 putForCreate(key, value);
1229 }
1230 }
1231
1232 /**
1233 * The put method for readObject. It does not resize the table,
1234 * update modCount, etc.
1235 */
1236 private void putForCreate(K key, V value)
1237 throws IOException
1238 {
1239 Object k = maskNull(key);
1240 Object[] tab = table;
1241 int len = tab.length;
1242 int i = hash(k, len);
1243
1244 Object item;
1245 while ( (item = tab[i]) != null) {
1246 if (item == k)
1247 throw new java.io.StreamCorruptedException();
1248 i = nextKeyIndex(i, len);
1249 }
1250 tab[i] = k;
1251 tab[i + 1] = value;
1252 }
1253 }
|