Path: blob/master/src/java.base/share/classes/java/util/IdentityHashMap.java
67794 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util;2627import java.io.ObjectInputStream;28import java.io.ObjectOutputStream;29import java.lang.reflect.Array;30import java.util.function.BiConsumer;31import java.util.function.BiFunction;32import java.util.function.Consumer;33import jdk.internal.access.SharedSecrets;3435/**36* This class implements the {@code Map} interface with a hash table, using37* reference-equality in place of object-equality when comparing keys (and38* values). In other words, in an {@code IdentityHashMap}, two keys39* {@code k1} and {@code k2} are considered equal if and only if40* {@code (k1==k2)}. (In normal {@code Map} implementations (like41* {@code HashMap}) two keys {@code k1} and {@code k2} are considered equal42* if and only if {@code (k1==null ? k2==null : k1.equals(k2))}.)43*44* <p><b>This class is <i>not</i> a general-purpose {@code Map}45* implementation! While this class implements the {@code Map} interface, it46* intentionally violates {@code Map's} general contract, which mandates the47* use of the {@code equals} method when comparing objects. This class is48* designed for use only in the rare cases wherein reference-equality49* semantics are required.</b>50*51* <p>A typical use of this class is <i>topology-preserving object graph52* transformations</i>, such as serialization or deep-copying. To perform such53* a transformation, a program must maintain a "node table" that keeps track54* of all the object references that have already been processed. The node55* table must not equate distinct objects even if they happen to be equal.56* Another typical use of this class is to maintain <i>proxy objects</i>. For57* example, a debugging facility might wish to maintain a proxy object for58* each object in the program being debugged.59*60* <p>This class provides all of the optional map operations, and permits61* {@code null} values and the {@code null} key. This class makes no62* guarantees as to the order of the map; in particular, it does not guarantee63* that the order will remain constant over time.64*65* <p>This class provides constant-time performance for the basic66* operations ({@code get} and {@code put}), assuming the system67* identity hash function ({@link System#identityHashCode(Object)})68* disperses elements properly among the buckets.69*70* <p>This class has one tuning parameter (which affects performance but not71* semantics): <i>expected maximum size</i>. This parameter is the maximum72* number of key-value mappings that the map is expected to hold. Internally,73* this parameter is used to determine the number of buckets initially74* comprising the hash table. The precise relationship between the expected75* maximum size and the number of buckets is unspecified.76*77* <p>If the size of the map (the number of key-value mappings) sufficiently78* exceeds the expected maximum size, the number of buckets is increased.79* Increasing the number of buckets ("rehashing") may be fairly expensive, so80* it pays to create identity hash maps with a sufficiently large expected81* maximum size. On the other hand, iteration over collection views requires82* time proportional to the number of buckets in the hash table, so it83* pays not to set the expected maximum size too high if you are especially84* concerned with iteration performance or memory usage.85*86* <p><strong>Note that this implementation is not synchronized.</strong>87* If multiple threads access an identity hash map concurrently, and at88* least one of the threads modifies the map structurally, it <i>must</i>89* be synchronized externally. (A structural modification is any operation90* that adds or deletes one or more mappings; merely changing the value91* associated with a key that an instance already contains is not a92* structural modification.) This is typically accomplished by93* synchronizing on some object that naturally encapsulates the map.94*95* If no such object exists, the map should be "wrapped" using the96* {@link Collections#synchronizedMap Collections.synchronizedMap}97* method. This is best done at creation time, to prevent accidental98* unsynchronized access to the map:<pre>99* Map m = Collections.synchronizedMap(new IdentityHashMap(...));</pre>100*101* <p>The iterators returned by the {@code iterator} method of the102* collections returned by all of this class's "collection view103* methods" are <i>fail-fast</i>: if the map is structurally modified104* at any time after the iterator is created, in any way except105* through the iterator's own {@code remove} method, the iterator106* will throw a {@link ConcurrentModificationException}. Thus, in the107* face of concurrent modification, the iterator fails quickly and108* cleanly, rather than risking arbitrary, non-deterministic behavior109* at an undetermined time in the future.110*111* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed112* as it is, generally speaking, impossible to make any hard guarantees in the113* presence of unsynchronized concurrent modification. Fail-fast iterators114* throw {@code ConcurrentModificationException} on a best-effort basis.115* Therefore, it would be wrong to write a program that depended on this116* exception for its correctness: <i>fail-fast iterators should be used only117* to detect bugs.</i>118*119* <p>This class is a member of the120* <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">121* Java Collections Framework</a>.122*123* @implNote124* <p>This is a simple <i>linear-probe</i> hash table,125* as described for example in texts by Sedgewick and Knuth. The array126* contains alternating keys and values, with keys at even indexes and values127* at odd indexes. (This arrangement has better locality for large128* tables than does using separate arrays.) For many Java implementations129* and operation mixes, this class will yield better performance than130* {@link HashMap}, which uses <i>chaining</i> rather than linear-probing.131*132* @see System#identityHashCode(Object)133* @see Object#hashCode()134* @see Collection135* @see Map136* @see HashMap137* @see TreeMap138* @author Doug Lea and Josh Bloch139* @since 1.4140*/141142public class IdentityHashMap<K,V>143extends AbstractMap<K,V>144implements Map<K,V>, java.io.Serializable, Cloneable145{146/**147* The initial capacity used by the no-args constructor.148* MUST be a power of two. The value 32 corresponds to the149* (specified) expected maximum size of 21, given a load factor150* of 2/3.151*/152private static final int DEFAULT_CAPACITY = 32;153154/**155* The minimum capacity, used if a lower value is implicitly specified156* by either of the constructors with arguments. The value 4 corresponds157* to an expected maximum size of 2, given a load factor of 2/3.158* MUST be a power of two.159*/160private static final int MINIMUM_CAPACITY = 4;161162/**163* The maximum capacity, used if a higher value is implicitly specified164* by either of the constructors with arguments.165* MUST be a power of two <= 1<<29.166*167* In fact, the map can hold no more than MAXIMUM_CAPACITY-1 items168* because it has to have at least one slot with the key == null169* in order to avoid infinite loops in get(), put(), remove()170*/171private static final int MAXIMUM_CAPACITY = 1 << 29;172173/**174* The table, resized as necessary. Length MUST always be a power of two.175*/176transient Object[] table; // non-private to simplify nested class access177178/**179* The number of key-value mappings contained in this identity hash map.180*181* @serial182*/183int size;184185/**186* The number of modifications, to support fast-fail iterators187*/188transient int modCount;189190/**191* Value representing null keys inside tables.192*/193static final Object NULL_KEY = new Object();194195/**196* Use NULL_KEY for key if it is null.197*/198private static Object maskNull(Object key) {199return (key == null ? NULL_KEY : key);200}201202/**203* Returns internal representation of null key back to caller as null.204*/205static final Object unmaskNull(Object key) {206return (key == NULL_KEY ? null : key);207}208209/**210* Constructs a new, empty identity hash map with a default expected211* maximum size (21).212*/213public IdentityHashMap() {214init(DEFAULT_CAPACITY);215}216217/**218* Constructs a new, empty map with the specified expected maximum size.219* Putting more than the expected number of key-value mappings into220* the map may cause the internal data structure to grow, which may be221* somewhat time-consuming.222*223* @param expectedMaxSize the expected maximum size of the map224* @throws IllegalArgumentException if {@code expectedMaxSize} is negative225*/226public IdentityHashMap(int expectedMaxSize) {227if (expectedMaxSize < 0)228throw new IllegalArgumentException("expectedMaxSize is negative: "229+ expectedMaxSize);230init(capacity(expectedMaxSize));231}232233/**234* Returns the appropriate capacity for the given expected maximum size.235* Returns the smallest power of two between MINIMUM_CAPACITY and236* MAXIMUM_CAPACITY, inclusive, that is greater than (3 *237* expectedMaxSize)/2, if such a number exists. Otherwise returns238* MAXIMUM_CAPACITY.239*/240private static int capacity(int expectedMaxSize) {241// assert expectedMaxSize >= 0;242return243(expectedMaxSize > MAXIMUM_CAPACITY / 3) ? MAXIMUM_CAPACITY :244(expectedMaxSize <= 2 * MINIMUM_CAPACITY / 3) ? MINIMUM_CAPACITY :245Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1));246}247248/**249* Initializes object to be an empty map with the specified initial250* capacity, which is assumed to be a power of two between251* MINIMUM_CAPACITY and MAXIMUM_CAPACITY inclusive.252*/253private void init(int initCapacity) {254// assert (initCapacity & -initCapacity) == initCapacity; // power of 2255// assert initCapacity >= MINIMUM_CAPACITY;256// assert initCapacity <= MAXIMUM_CAPACITY;257258table = new Object[2 * initCapacity];259}260261/**262* Constructs a new identity hash map containing the keys-value mappings263* in the specified map.264*265* @param m the map whose mappings are to be placed into this map266* @throws NullPointerException if the specified map is null267*/268public IdentityHashMap(Map<? extends K, ? extends V> m) {269// Allow for a bit of growth270this((int) ((1 + m.size()) * 1.1));271putAll(m);272}273274/**275* Returns the number of key-value mappings in this identity hash map.276*277* @return the number of key-value mappings in this map278*/279public int size() {280return size;281}282283/**284* Returns {@code true} if this identity hash map contains no key-value285* mappings.286*287* @return {@code true} if this identity hash map contains no key-value288* mappings289*/290public boolean isEmpty() {291return size == 0;292}293294/**295* Returns index for Object x.296*/297private static int hash(Object x, int length) {298int h = System.identityHashCode(x);299// Multiply by -254 to use the hash LSB and to ensure index is even300return ((h << 1) - (h << 8)) & (length - 1);301}302303/**304* Circularly traverses table of size len.305*/306private static int nextKeyIndex(int i, int len) {307return (i + 2 < len ? i + 2 : 0);308}309310/**311* Returns the value to which the specified key is mapped,312* or {@code null} if this map contains no mapping for the key.313*314* <p>More formally, if this map contains a mapping from a key315* {@code k} to a value {@code v} such that {@code (key == k)},316* then this method returns {@code v}; otherwise it returns317* {@code null}. (There can be at most one such mapping.)318*319* <p>A return value of {@code null} does not <i>necessarily</i>320* indicate that the map contains no mapping for the key; it's also321* possible that the map explicitly maps the key to {@code null}.322* The {@link #containsKey containsKey} operation may be used to323* distinguish these two cases.324*325* @see #put(Object, Object)326*/327@SuppressWarnings("unchecked")328public V get(Object key) {329Object k = maskNull(key);330Object[] tab = table;331int len = tab.length;332int i = hash(k, len);333while (true) {334Object item = tab[i];335if (item == k)336return (V) tab[i + 1];337if (item == null)338return null;339i = nextKeyIndex(i, len);340}341}342343/**344* Tests whether the specified object reference is a key in this identity345* hash map.346*347* @param key possible key348* @return {@code true} if the specified object reference is a key349* in this map350* @see #containsValue(Object)351*/352public boolean containsKey(Object key) {353Object k = maskNull(key);354Object[] tab = table;355int len = tab.length;356int i = hash(k, len);357while (true) {358Object item = tab[i];359if (item == k)360return true;361if (item == null)362return false;363i = nextKeyIndex(i, len);364}365}366367/**368* Tests whether the specified object reference is a value in this identity369* hash map.370*371* @param value value whose presence in this map is to be tested372* @return {@code true} if this map maps one or more keys to the373* specified object reference374* @see #containsKey(Object)375*/376public boolean containsValue(Object value) {377Object[] tab = table;378for (int i = 1; i < tab.length; i += 2)379if (tab[i] == value && tab[i - 1] != null)380return true;381382return false;383}384385/**386* Tests if the specified key-value mapping is in the map.387*388* @param key possible key389* @param value possible value390* @return {@code true} if and only if the specified key-value391* mapping is in the map392*/393private boolean containsMapping(Object key, Object value) {394Object k = maskNull(key);395Object[] tab = table;396int len = tab.length;397int i = hash(k, len);398while (true) {399Object item = tab[i];400if (item == k)401return tab[i + 1] == value;402if (item == null)403return false;404i = nextKeyIndex(i, len);405}406}407408/**409* Associates the specified value with the specified key in this identity410* hash map. If the map previously contained a mapping for the key, the411* old value is replaced.412*413* @param key the key with which the specified value is to be associated414* @param value the value to be associated with the specified key415* @return the previous value associated with {@code key}, or416* {@code null} if there was no mapping for {@code key}.417* (A {@code null} return can also indicate that the map418* previously associated {@code null} with {@code key}.)419* @see Object#equals(Object)420* @see #get(Object)421* @see #containsKey(Object)422*/423public V put(K key, V value) {424final Object k = maskNull(key);425426retryAfterResize: for (;;) {427final Object[] tab = table;428final int len = tab.length;429int i = hash(k, len);430431for (Object item; (item = tab[i]) != null;432i = nextKeyIndex(i, len)) {433if (item == k) {434@SuppressWarnings("unchecked")435V oldValue = (V) tab[i + 1];436tab[i + 1] = value;437return oldValue;438}439}440441final int s = size + 1;442// Use optimized form of 3 * s.443// Next capacity is len, 2 * current capacity.444if (s + (s << 1) > len && resize(len))445continue retryAfterResize;446447modCount++;448tab[i] = k;449tab[i + 1] = value;450size = s;451return null;452}453}454455/**456* Resizes the table if necessary to hold given capacity.457*458* @param newCapacity the new capacity, must be a power of two.459* @return whether a resize did in fact take place460*/461private boolean resize(int newCapacity) {462// assert (newCapacity & -newCapacity) == newCapacity; // power of 2463int newLength = newCapacity * 2;464465Object[] oldTable = table;466int oldLength = oldTable.length;467if (oldLength == 2 * MAXIMUM_CAPACITY) { // can't expand any further468if (size == MAXIMUM_CAPACITY - 1)469throw new IllegalStateException("Capacity exhausted.");470return false;471}472if (oldLength >= newLength)473return false;474475Object[] newTable = new Object[newLength];476477for (int j = 0; j < oldLength; j += 2) {478Object key = oldTable[j];479if (key != null) {480Object value = oldTable[j+1];481oldTable[j] = null;482oldTable[j+1] = null;483int i = hash(key, newLength);484while (newTable[i] != null)485i = nextKeyIndex(i, newLength);486newTable[i] = key;487newTable[i + 1] = value;488}489}490table = newTable;491return true;492}493494/**495* Copies all of the mappings from the specified map to this map.496* These mappings will replace any mappings that this map had for497* any of the keys currently in the specified map.498*499* @param m mappings to be stored in this map500* @throws NullPointerException if the specified map is null501*/502public void putAll(Map<? extends K, ? extends V> m) {503int n = m.size();504if (n == 0)505return;506if (n > size)507resize(capacity(n)); // conservatively pre-expand508509for (Entry<? extends K, ? extends V> e : m.entrySet())510put(e.getKey(), e.getValue());511}512513/**514* Removes the mapping for this key from this map if present.515*516* @param key key whose mapping is to be removed from the map517* @return the previous value associated with {@code key}, or518* {@code null} if there was no mapping for {@code key}.519* (A {@code null} return can also indicate that the map520* previously associated {@code null} with {@code key}.)521*/522public V remove(Object key) {523Object k = maskNull(key);524Object[] tab = table;525int len = tab.length;526int i = hash(k, len);527528while (true) {529Object item = tab[i];530if (item == k) {531modCount++;532size--;533@SuppressWarnings("unchecked")534V oldValue = (V) tab[i + 1];535tab[i + 1] = null;536tab[i] = null;537closeDeletion(i);538return oldValue;539}540if (item == null)541return null;542i = nextKeyIndex(i, len);543}544}545546/**547* Removes the specified key-value mapping from the map if it is present.548*549* @param key possible key550* @param value possible value551* @return {@code true} if and only if the specified key-value552* mapping was in the map553*/554private boolean removeMapping(Object key, Object value) {555Object k = maskNull(key);556Object[] tab = table;557int len = tab.length;558int i = hash(k, len);559560while (true) {561Object item = tab[i];562if (item == k) {563if (tab[i + 1] != value)564return false;565modCount++;566size--;567tab[i] = null;568tab[i + 1] = null;569closeDeletion(i);570return true;571}572if (item == null)573return false;574i = nextKeyIndex(i, len);575}576}577578/**579* Rehash all possibly-colliding entries following a580* deletion. This preserves the linear-probe581* collision properties required by get, put, etc.582*583* @param d the index of a newly empty deleted slot584*/585private void closeDeletion(int d) {586// Adapted from Knuth Section 6.4 Algorithm R587Object[] tab = table;588int len = tab.length;589590// Look for items to swap into newly vacated slot591// starting at index immediately following deletion,592// and continuing until a null slot is seen, indicating593// the end of a run of possibly-colliding keys.594Object item;595for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;596i = nextKeyIndex(i, len) ) {597// The following test triggers if the item at slot i (which598// hashes to be at slot r) should take the spot vacated by d.599// If so, we swap it in, and then continue with d now at the600// newly vacated i. This process will terminate when we hit601// the null slot at the end of this run.602// The test is messy because we are using a circular table.603int r = hash(item, len);604if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {605tab[d] = item;606tab[d + 1] = tab[i + 1];607tab[i] = null;608tab[i + 1] = null;609d = i;610}611}612}613614/**615* Removes all of the mappings from this map.616* The map will be empty after this call returns.617*/618public void clear() {619modCount++;620Object[] tab = table;621for (int i = 0; i < tab.length; i++)622tab[i] = null;623size = 0;624}625626/**627* Compares the specified object with this map for equality. Returns628* {@code true} if the given object is also a map and the two maps629* represent identical object-reference mappings. More formally, this630* map is equal to another map {@code m} if and only if631* {@code this.entrySet().equals(m.entrySet())}.632*633* <p><b>Owing to the reference-equality-based semantics of this map it is634* possible that the symmetry and transitivity requirements of the635* {@code Object.equals} contract may be violated if this map is compared636* to a normal map. However, the {@code Object.equals} contract is637* guaranteed to hold among {@code IdentityHashMap} instances.</b>638*639* @param o object to be compared for equality with this map640* @return {@code true} if the specified object is equal to this map641* @see Object#equals(Object)642*/643public boolean equals(Object o) {644if (o == this) {645return true;646} else if (o instanceof IdentityHashMap<?, ?> m) {647if (m.size() != size)648return false;649650Object[] tab = m.table;651for (int i = 0; i < tab.length; i+=2) {652Object k = tab[i];653if (k != null && !containsMapping(k, tab[i + 1]))654return false;655}656return true;657} else if (o instanceof Map<?, ?> m) {658return entrySet().equals(m.entrySet());659} else {660return false; // o is not a Map661}662}663664/**665* Returns the hash code value for this map. The hash code of a map is666* defined to be the sum of the hash codes of each entry in the map's667* {@code entrySet()} view. This ensures that {@code m1.equals(m2)}668* implies that {@code m1.hashCode()==m2.hashCode()} for any two669* {@code IdentityHashMap} instances {@code m1} and {@code m2}, as670* required by the general contract of {@link Object#hashCode}.671*672* <p><b>Owing to the reference-equality-based semantics of the673* {@code Map.Entry} instances in the set returned by this map's674* {@code entrySet} method, it is possible that the contractual675* requirement of {@code Object.hashCode} mentioned in the previous676* paragraph will be violated if one of the two objects being compared is677* an {@code IdentityHashMap} instance and the other is a normal map.</b>678*679* @return the hash code value for this map680* @see Object#equals(Object)681* @see #equals(Object)682*/683public int hashCode() {684int result = 0;685Object[] tab = table;686for (int i = 0; i < tab.length; i +=2) {687Object key = tab[i];688if (key != null) {689Object k = unmaskNull(key);690result += System.identityHashCode(k) ^691System.identityHashCode(tab[i + 1]);692}693}694return result;695}696697/**698* Returns a shallow copy of this identity hash map: the keys and values699* themselves are not cloned.700*701* @return a shallow copy of this map702*/703public Object clone() {704try {705IdentityHashMap<?,?> m = (IdentityHashMap<?,?>) super.clone();706m.entrySet = null;707m.table = table.clone();708return m;709} catch (CloneNotSupportedException e) {710throw new InternalError(e);711}712}713714private abstract class IdentityHashMapIterator<T> implements Iterator<T> {715int index = (size != 0 ? 0 : table.length); // current slot.716int expectedModCount = modCount; // to support fast-fail717int lastReturnedIndex = -1; // to allow remove()718boolean indexValid; // To avoid unnecessary next computation719Object[] traversalTable = table; // reference to main table or copy720721public boolean hasNext() {722Object[] tab = traversalTable;723for (int i = index; i < tab.length; i+=2) {724Object key = tab[i];725if (key != null) {726index = i;727return indexValid = true;728}729}730index = tab.length;731return false;732}733734protected int nextIndex() {735if (modCount != expectedModCount)736throw new ConcurrentModificationException();737if (!indexValid && !hasNext())738throw new NoSuchElementException();739740indexValid = false;741lastReturnedIndex = index;742index += 2;743return lastReturnedIndex;744}745746public void remove() {747if (lastReturnedIndex == -1)748throw new IllegalStateException();749if (modCount != expectedModCount)750throw new ConcurrentModificationException();751752expectedModCount = ++modCount;753int deletedSlot = lastReturnedIndex;754lastReturnedIndex = -1;755// back up index to revisit new contents after deletion756index = deletedSlot;757indexValid = false;758759// Removal code proceeds as in closeDeletion except that760// it must catch the rare case where an element already761// seen is swapped into a vacant slot that will be later762// traversed by this iterator. We cannot allow future763// next() calls to return it again. The likelihood of764// this occurring under 2/3 load factor is very slim, but765// when it does happen, we must make a copy of the rest of766// the table to use for the rest of the traversal. Since767// this can only happen when we are near the end of the table,768// even in these rare cases, this is not very expensive in769// time or space.770771Object[] tab = traversalTable;772int len = tab.length;773774int d = deletedSlot;775Object key = tab[d];776tab[d] = null; // vacate the slot777tab[d + 1] = null;778779// If traversing a copy, remove in real table.780// We can skip gap-closure on copy.781if (tab != IdentityHashMap.this.table) {782IdentityHashMap.this.remove(key);783expectedModCount = modCount;784return;785}786787size--;788789Object item;790for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;791i = nextKeyIndex(i, len)) {792int r = hash(item, len);793// See closeDeletion for explanation of this conditional794if ((i < r && (r <= d || d <= i)) ||795(r <= d && d <= i)) {796797// If we are about to swap an already-seen element798// into a slot that may later be returned by next(),799// then clone the rest of table for use in future800// next() calls. It is OK that our copy will have801// a gap in the "wrong" place, since it will never802// be used for searching anyway.803804if (i < deletedSlot && d >= deletedSlot &&805traversalTable == IdentityHashMap.this.table) {806int remaining = len - deletedSlot;807Object[] newTable = new Object[remaining];808System.arraycopy(tab, deletedSlot,809newTable, 0, remaining);810traversalTable = newTable;811index = 0;812}813814tab[d] = item;815tab[d + 1] = tab[i + 1];816tab[i] = null;817tab[i + 1] = null;818d = i;819}820}821}822}823824private class KeyIterator extends IdentityHashMapIterator<K> {825@SuppressWarnings("unchecked")826public K next() {827return (K) unmaskNull(traversalTable[nextIndex()]);828}829}830831private class ValueIterator extends IdentityHashMapIterator<V> {832@SuppressWarnings("unchecked")833public V next() {834return (V) traversalTable[nextIndex() + 1];835}836}837838private class EntryIterator839extends IdentityHashMapIterator<Map.Entry<K,V>>840{841private Entry lastReturnedEntry;842843public Map.Entry<K,V> next() {844lastReturnedEntry = new Entry(nextIndex());845return lastReturnedEntry;846}847848public void remove() {849lastReturnedIndex =850((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);851super.remove();852lastReturnedEntry.index = lastReturnedIndex;853lastReturnedEntry = null;854}855856private class Entry implements Map.Entry<K,V> {857private int index;858859private Entry(int index) {860this.index = index;861}862863@SuppressWarnings("unchecked")864public K getKey() {865checkIndexForEntryUse();866return (K) unmaskNull(traversalTable[index]);867}868869@SuppressWarnings("unchecked")870public V getValue() {871checkIndexForEntryUse();872return (V) traversalTable[index+1];873}874875@SuppressWarnings("unchecked")876public V setValue(V value) {877checkIndexForEntryUse();878V oldValue = (V) traversalTable[index+1];879traversalTable[index+1] = value;880// if shadowing, force into main table881if (traversalTable != IdentityHashMap.this.table)882put((K) traversalTable[index], value);883return oldValue;884}885886public boolean equals(Object o) {887if (index < 0)888return super.equals(o);889890return o instanceof Map.Entry<?, ?> e891&& e.getKey() == unmaskNull(traversalTable[index])892&& e.getValue() == traversalTable[index+1];893}894895public int hashCode() {896if (lastReturnedIndex < 0)897return super.hashCode();898899return (System.identityHashCode(unmaskNull(traversalTable[index])) ^900System.identityHashCode(traversalTable[index+1]));901}902903public String toString() {904if (index < 0)905return super.toString();906907return (unmaskNull(traversalTable[index]) + "="908+ traversalTable[index+1]);909}910911private void checkIndexForEntryUse() {912if (index < 0)913throw new IllegalStateException("Entry was removed");914}915}916}917918// Views919920/**921* This field is initialized to contain an instance of the entry set922* view the first time this view is requested. The view is stateless,923* so there's no reason to create more than one.924*/925private transient Set<Map.Entry<K,V>> entrySet;926927/**928* Returns an identity-based set view of the keys contained in this map.929* The set is backed by the map, so changes to the map are reflected in930* the set, and vice-versa. If the map is modified while an iteration931* over the set is in progress, the results of the iteration are932* undefined. The set supports element removal, which removes the933* corresponding mapping from the map, via the {@code Iterator.remove},934* {@code Set.remove}, {@code removeAll}, {@code retainAll}, and935* {@code clear} methods. It does not support the {@code add} or936* {@code addAll} methods.937*938* <p><b>While the object returned by this method implements the939* {@code Set} interface, it does <i>not</i> obey {@code Set's} general940* contract. Like its backing map, the set returned by this method941* defines element equality as reference-equality rather than942* object-equality. This affects the behavior of its {@code contains},943* {@code remove}, {@code containsAll}, {@code equals}, and944* {@code hashCode} methods.</b>945*946* <p><b>The {@code equals} method of the returned set returns {@code true}947* only if the specified object is a set containing exactly the same948* object references as the returned set. The symmetry and transitivity949* requirements of the {@code Object.equals} contract may be violated if950* the set returned by this method is compared to a normal set. However,951* the {@code Object.equals} contract is guaranteed to hold among sets952* returned by this method.</b>953*954* <p>The {@code hashCode} method of the returned set returns the sum of955* the <i>identity hashcodes</i> of the elements in the set, rather than956* the sum of their hashcodes. This is mandated by the change in the957* semantics of the {@code equals} method, in order to enforce the958* general contract of the {@code Object.hashCode} method among sets959* returned by this method.960*961* @return an identity-based set view of the keys contained in this map962* @see Object#equals(Object)963* @see System#identityHashCode(Object)964*/965public Set<K> keySet() {966Set<K> ks = keySet;967if (ks == null) {968ks = new KeySet();969keySet = ks;970}971return ks;972}973974private class KeySet extends AbstractSet<K> {975public Iterator<K> iterator() {976return new KeyIterator();977}978public int size() {979return size;980}981public boolean contains(Object o) {982return containsKey(o);983}984public boolean remove(Object o) {985int oldSize = size;986IdentityHashMap.this.remove(o);987return size != oldSize;988}989/*990* Must revert from AbstractSet's impl to AbstractCollection's, as991* the former contains an optimization that results in incorrect992* behavior when c is a smaller "normal" (non-identity-based) Set.993*/994public boolean removeAll(Collection<?> c) {995Objects.requireNonNull(c);996boolean modified = false;997for (Iterator<K> i = iterator(); i.hasNext(); ) {998if (c.contains(i.next())) {999i.remove();1000modified = true;1001}1002}1003return modified;1004}1005public void clear() {1006IdentityHashMap.this.clear();1007}1008public int hashCode() {1009int result = 0;1010for (K key : this)1011result += System.identityHashCode(key);1012return result;1013}1014public Object[] toArray() {1015return toArray(new Object[0]);1016}1017@SuppressWarnings("unchecked")1018public <T> T[] toArray(T[] a) {1019int expectedModCount = modCount;1020int size = size();1021if (a.length < size)1022a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);1023Object[] tab = table;1024int ti = 0;1025for (int si = 0; si < tab.length; si += 2) {1026Object key;1027if ((key = tab[si]) != null) { // key present ?1028// more elements than expected -> concurrent modification from other thread1029if (ti >= size) {1030throw new ConcurrentModificationException();1031}1032a[ti++] = (T) unmaskNull(key); // unmask key1033}1034}1035// fewer elements than expected or concurrent modification from other thread detected1036if (ti < size || expectedModCount != modCount) {1037throw new ConcurrentModificationException();1038}1039// final null marker as per spec1040if (ti < a.length) {1041a[ti] = null;1042}1043return a;1044}10451046public Spliterator<K> spliterator() {1047return new KeySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);1048}1049}10501051/**1052* Returns a {@link Collection} view of the values contained in this map.1053* The collection is backed by the map, so changes to the map are1054* reflected in the collection, and vice-versa. If the map is1055* modified while an iteration over the collection is in progress,1056* the results of the iteration are undefined. The collection1057* supports element removal, which removes the corresponding1058* mapping from the map, via the {@code Iterator.remove},1059* {@code Collection.remove}, {@code removeAll},1060* {@code retainAll} and {@code clear} methods. It does not1061* support the {@code add} or {@code addAll} methods.1062*1063* <p><b>While the object returned by this method implements the1064* {@code Collection} interface, it does <i>not</i> obey1065* {@code Collection's} general contract. Like its backing map,1066* the collection returned by this method defines element equality as1067* reference-equality rather than object-equality. This affects the1068* behavior of its {@code contains}, {@code remove} and1069* {@code containsAll} methods.</b>1070*/1071public Collection<V> values() {1072Collection<V> vs = values;1073if (vs == null) {1074vs = new Values();1075values = vs;1076}1077return vs;1078}10791080private class Values extends AbstractCollection<V> {1081public Iterator<V> iterator() {1082return new ValueIterator();1083}1084public int size() {1085return size;1086}1087public boolean contains(Object o) {1088return containsValue(o);1089}1090public boolean remove(Object o) {1091for (Iterator<V> i = iterator(); i.hasNext(); ) {1092if (i.next() == o) {1093i.remove();1094return true;1095}1096}1097return false;1098}1099public void clear() {1100IdentityHashMap.this.clear();1101}1102public Object[] toArray() {1103return toArray(new Object[0]);1104}1105@SuppressWarnings("unchecked")1106public <T> T[] toArray(T[] a) {1107int expectedModCount = modCount;1108int size = size();1109if (a.length < size)1110a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);1111Object[] tab = table;1112int ti = 0;1113for (int si = 0; si < tab.length; si += 2) {1114if (tab[si] != null) { // key present ?1115// more elements than expected -> concurrent modification from other thread1116if (ti >= size) {1117throw new ConcurrentModificationException();1118}1119a[ti++] = (T) tab[si+1]; // copy value1120}1121}1122// fewer elements than expected or concurrent modification from other thread detected1123if (ti < size || expectedModCount != modCount) {1124throw new ConcurrentModificationException();1125}1126// final null marker as per spec1127if (ti < a.length) {1128a[ti] = null;1129}1130return a;1131}11321133public Spliterator<V> spliterator() {1134return new ValueSpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);1135}1136}11371138/**1139* Returns a {@link Set} view of the mappings contained in this map.1140* Each element in the returned set is a reference-equality-based1141* {@code Map.Entry}. The set is backed by the map, so changes1142* to the map are reflected in the set, and vice-versa. If the1143* map is modified while an iteration over the set is in progress,1144* the results of the iteration are undefined. The set supports1145* element removal, which removes the corresponding mapping from1146* the map, via the {@code Iterator.remove}, {@code Set.remove},1147* {@code removeAll}, {@code retainAll} and {@code clear}1148* methods. It does not support the {@code add} or1149* {@code addAll} methods.1150*1151* <p>Like the backing map, the {@code Map.Entry} objects in the set1152* returned by this method define key and value equality as1153* reference-equality rather than object-equality. This affects the1154* behavior of the {@code equals} and {@code hashCode} methods of these1155* {@code Map.Entry} objects. A reference-equality based {@code Map.Entry1156* e} is equal to an object {@code o} if and only if {@code o} is a1157* {@code Map.Entry} and {@code e.getKey()==o.getKey() &&1158* e.getValue()==o.getValue()}. To accommodate these equals1159* semantics, the {@code hashCode} method returns1160* {@code System.identityHashCode(e.getKey()) ^1161* System.identityHashCode(e.getValue())}.1162*1163* <p><b>Owing to the reference-equality-based semantics of the1164* {@code Map.Entry} instances in the set returned by this method,1165* it is possible that the symmetry and transitivity requirements of1166* the {@link Object#equals(Object)} contract may be violated if any of1167* the entries in the set is compared to a normal map entry, or if1168* the set returned by this method is compared to a set of normal map1169* entries (such as would be returned by a call to this method on a normal1170* map). However, the {@code Object.equals} contract is guaranteed to1171* hold among identity-based map entries, and among sets of such entries.1172* </b>1173*1174* @return a set view of the identity-mappings contained in this map1175*/1176public Set<Map.Entry<K,V>> entrySet() {1177Set<Map.Entry<K,V>> es = entrySet;1178if (es != null)1179return es;1180else1181return entrySet = new EntrySet();1182}11831184private class EntrySet extends AbstractSet<Map.Entry<K,V>> {1185public Iterator<Map.Entry<K,V>> iterator() {1186return new EntryIterator();1187}1188public boolean contains(Object o) {1189return o instanceof Entry<?, ?> entry1190&& containsMapping(entry.getKey(), entry.getValue());1191}1192public boolean remove(Object o) {1193return o instanceof Entry<?, ?> entry1194&& removeMapping(entry.getKey(), entry.getValue());1195}1196public int size() {1197return size;1198}1199public void clear() {1200IdentityHashMap.this.clear();1201}1202/*1203* Must revert from AbstractSet's impl to AbstractCollection's, as1204* the former contains an optimization that results in incorrect1205* behavior when c is a smaller "normal" (non-identity-based) Set.1206*/1207public boolean removeAll(Collection<?> c) {1208Objects.requireNonNull(c);1209boolean modified = false;1210for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) {1211if (c.contains(i.next())) {1212i.remove();1213modified = true;1214}1215}1216return modified;1217}12181219public Object[] toArray() {1220return toArray(new Object[0]);1221}12221223@SuppressWarnings("unchecked")1224public <T> T[] toArray(T[] a) {1225int expectedModCount = modCount;1226int size = size();1227if (a.length < size)1228a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);1229Object[] tab = table;1230int ti = 0;1231for (int si = 0; si < tab.length; si += 2) {1232Object key;1233if ((key = tab[si]) != null) { // key present ?1234// more elements than expected -> concurrent modification from other thread1235if (ti >= size) {1236throw new ConcurrentModificationException();1237}1238a[ti++] = (T) new AbstractMap.SimpleEntry<>(unmaskNull(key), tab[si + 1]);1239}1240}1241// fewer elements than expected or concurrent modification from other thread detected1242if (ti < size || expectedModCount != modCount) {1243throw new ConcurrentModificationException();1244}1245// final null marker as per spec1246if (ti < a.length) {1247a[ti] = null;1248}1249return a;1250}12511252public Spliterator<Map.Entry<K,V>> spliterator() {1253return new EntrySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);1254}1255}12561257@java.io.Serial1258private static final long serialVersionUID = 8188218128353913216L;12591260/**1261* Saves the state of the {@code IdentityHashMap} instance to a stream1262* (i.e., serializes it).1263*1264* @serialData The <i>size</i> of the HashMap (the number of key-value1265* mappings) ({@code int}), followed by the key (Object) and1266* value (Object) for each key-value mapping represented by the1267* IdentityHashMap. The key-value mappings are emitted in no1268* particular order.1269*/1270@java.io.Serial1271private void writeObject(ObjectOutputStream s)1272throws java.io.IOException {1273// Write out size (number of mappings) and any hidden stuff1274s.defaultWriteObject();12751276// Write out size again (maintained for backward compatibility)1277s.writeInt(size);12781279// Write out keys and values (alternating)1280Object[] tab = table;1281for (int i = 0; i < tab.length; i += 2) {1282Object key = tab[i];1283if (key != null) {1284s.writeObject(unmaskNull(key));1285s.writeObject(tab[i + 1]);1286}1287}1288}12891290/**1291* Reconstitutes the {@code IdentityHashMap} instance from a stream (i.e.,1292* deserializes it).1293*/1294@java.io.Serial1295private void readObject(ObjectInputStream s)1296throws java.io.IOException, ClassNotFoundException {1297// Size (number of mappings) is written to the stream twice1298// Read first size value and ignore it1299s.readFields();13001301// Read second size value, validate and assign to size field1302int size = s.readInt();1303if (size < 0)1304throw new java.io.StreamCorruptedException1305("Illegal mappings count: " + size);1306int cap = capacity(size);1307SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, cap*2);1308this.size = size;1309init(cap);13101311// Read the keys and values, and put the mappings in the table1312for (int i=0; i<size; i++) {1313@SuppressWarnings("unchecked")1314K key = (K) s.readObject();1315@SuppressWarnings("unchecked")1316V value = (V) s.readObject();1317putForCreate(key, value);1318}1319}13201321/**1322* The put method for readObject. It does not resize the table,1323* update modCount, etc.1324*/1325private void putForCreate(K key, V value)1326throws java.io.StreamCorruptedException1327{1328Object k = maskNull(key);1329Object[] tab = table;1330int len = tab.length;1331int i = hash(k, len);13321333Object item;1334while ( (item = tab[i]) != null) {1335if (item == k)1336throw new java.io.StreamCorruptedException();1337i = nextKeyIndex(i, len);1338}1339tab[i] = k;1340tab[i + 1] = value;1341}13421343@SuppressWarnings("unchecked")1344@Override1345public void forEach(BiConsumer<? super K, ? super V> action) {1346Objects.requireNonNull(action);1347int expectedModCount = modCount;13481349Object[] t = table;1350for (int index = 0; index < t.length; index += 2) {1351Object k = t[index];1352if (k != null) {1353action.accept((K) unmaskNull(k), (V) t[index + 1]);1354}13551356if (modCount != expectedModCount) {1357throw new ConcurrentModificationException();1358}1359}1360}13611362@SuppressWarnings("unchecked")1363@Override1364public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {1365Objects.requireNonNull(function);1366int expectedModCount = modCount;13671368Object[] t = table;1369for (int index = 0; index < t.length; index += 2) {1370Object k = t[index];1371if (k != null) {1372t[index + 1] = function.apply((K) unmaskNull(k), (V) t[index + 1]);1373}13741375if (modCount != expectedModCount) {1376throw new ConcurrentModificationException();1377}1378}1379}13801381/**1382* Similar form as array-based Spliterators, but skips blank elements,1383* and guestimates size as decreasing by half per split.1384*/1385static class IdentityHashMapSpliterator<K,V> {1386final IdentityHashMap<K,V> map;1387int index; // current index, modified on advance/split1388int fence; // -1 until first use; then one past last index1389int est; // size estimate1390int expectedModCount; // initialized when fence set13911392IdentityHashMapSpliterator(IdentityHashMap<K,V> map, int origin,1393int fence, int est, int expectedModCount) {1394this.map = map;1395this.index = origin;1396this.fence = fence;1397this.est = est;1398this.expectedModCount = expectedModCount;1399}14001401final int getFence() { // initialize fence and size on first use1402int hi;1403if ((hi = fence) < 0) {1404est = map.size;1405expectedModCount = map.modCount;1406hi = fence = map.table.length;1407}1408return hi;1409}14101411public final long estimateSize() {1412getFence(); // force init1413return (long) est;1414}1415}14161417static final class KeySpliterator<K,V>1418extends IdentityHashMapSpliterator<K,V>1419implements Spliterator<K> {1420KeySpliterator(IdentityHashMap<K,V> map, int origin, int fence, int est,1421int expectedModCount) {1422super(map, origin, fence, est, expectedModCount);1423}14241425public KeySpliterator<K,V> trySplit() {1426int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;1427return (lo >= mid) ? null :1428new KeySpliterator<>(map, lo, index = mid, est >>>= 1,1429expectedModCount);1430}14311432@SuppressWarnings("unchecked")1433public void forEachRemaining(Consumer<? super K> action) {1434if (action == null)1435throw new NullPointerException();1436int i, hi, mc; Object key;1437IdentityHashMap<K,V> m; Object[] a;1438if ((m = map) != null && (a = m.table) != null &&1439(i = index) >= 0 && (index = hi = getFence()) <= a.length) {1440for (; i < hi; i += 2) {1441if ((key = a[i]) != null)1442action.accept((K)unmaskNull(key));1443}1444if (m.modCount == expectedModCount)1445return;1446}1447throw new ConcurrentModificationException();1448}14491450@SuppressWarnings("unchecked")1451public boolean tryAdvance(Consumer<? super K> action) {1452if (action == null)1453throw new NullPointerException();1454Object[] a = map.table;1455int hi = getFence();1456while (index < hi) {1457Object key = a[index];1458index += 2;1459if (key != null) {1460action.accept((K)unmaskNull(key));1461if (map.modCount != expectedModCount)1462throw new ConcurrentModificationException();1463return true;1464}1465}1466return false;1467}14681469public int characteristics() {1470return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;1471}1472}14731474static final class ValueSpliterator<K,V>1475extends IdentityHashMapSpliterator<K,V>1476implements Spliterator<V> {1477ValueSpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,1478int expectedModCount) {1479super(m, origin, fence, est, expectedModCount);1480}14811482public ValueSpliterator<K,V> trySplit() {1483int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;1484return (lo >= mid) ? null :1485new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,1486expectedModCount);1487}14881489public void forEachRemaining(Consumer<? super V> action) {1490if (action == null)1491throw new NullPointerException();1492int i, hi, mc;1493IdentityHashMap<K,V> m; Object[] a;1494if ((m = map) != null && (a = m.table) != null &&1495(i = index) >= 0 && (index = hi = getFence()) <= a.length) {1496for (; i < hi; i += 2) {1497if (a[i] != null) {1498@SuppressWarnings("unchecked") V v = (V)a[i+1];1499action.accept(v);1500}1501}1502if (m.modCount == expectedModCount)1503return;1504}1505throw new ConcurrentModificationException();1506}15071508public boolean tryAdvance(Consumer<? super V> action) {1509if (action == null)1510throw new NullPointerException();1511Object[] a = map.table;1512int hi = getFence();1513while (index < hi) {1514Object key = a[index];1515@SuppressWarnings("unchecked") V v = (V)a[index+1];1516index += 2;1517if (key != null) {1518action.accept(v);1519if (map.modCount != expectedModCount)1520throw new ConcurrentModificationException();1521return true;1522}1523}1524return false;1525}15261527public int characteristics() {1528return (fence < 0 || est == map.size ? SIZED : 0);1529}15301531}15321533static final class EntrySpliterator<K,V>1534extends IdentityHashMapSpliterator<K,V>1535implements Spliterator<Map.Entry<K,V>> {1536EntrySpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,1537int expectedModCount) {1538super(m, origin, fence, est, expectedModCount);1539}15401541public EntrySpliterator<K,V> trySplit() {1542int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;1543return (lo >= mid) ? null :1544new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,1545expectedModCount);1546}15471548public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {1549if (action == null)1550throw new NullPointerException();1551int i, hi, mc;1552IdentityHashMap<K,V> m; Object[] a;1553if ((m = map) != null && (a = m.table) != null &&1554(i = index) >= 0 && (index = hi = getFence()) <= a.length) {1555for (; i < hi; i += 2) {1556Object key = a[i];1557if (key != null) {1558@SuppressWarnings("unchecked") K k =1559(K)unmaskNull(key);1560@SuppressWarnings("unchecked") V v = (V)a[i+1];1561action.accept1562(new AbstractMap.SimpleImmutableEntry<>(k, v));15631564}1565}1566if (m.modCount == expectedModCount)1567return;1568}1569throw new ConcurrentModificationException();1570}15711572public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {1573if (action == null)1574throw new NullPointerException();1575Object[] a = map.table;1576int hi = getFence();1577while (index < hi) {1578Object key = a[index];1579@SuppressWarnings("unchecked") V v = (V)a[index+1];1580index += 2;1581if (key != null) {1582@SuppressWarnings("unchecked") K k =1583(K)unmaskNull(key);1584action.accept1585(new AbstractMap.SimpleImmutableEntry<>(k, v));1586if (map.modCount != expectedModCount)1587throw new ConcurrentModificationException();1588return true;1589}1590}1591return false;1592}15931594public int characteristics() {1595return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;1596}1597}15981599}160016011602