Path: blob/master/src/java.base/share/classes/java/util/IdentityHashMap.java
41152 views
/*1* Copyright (c) 2000, 2020, 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.lang.reflect.Array;28import java.util.function.BiConsumer;29import java.util.function.BiFunction;30import java.util.function.Consumer;31import jdk.internal.access.SharedSecrets;3233/**34* This class implements the {@code Map} interface with a hash table, using35* reference-equality in place of object-equality when comparing keys (and36* values). In other words, in an {@code IdentityHashMap}, two keys37* {@code k1} and {@code k2} are considered equal if and only if38* {@code (k1==k2)}. (In normal {@code Map} implementations (like39* {@code HashMap}) two keys {@code k1} and {@code k2} are considered equal40* if and only if {@code (k1==null ? k2==null : k1.equals(k2))}.)41*42* <p><b>This class is <i>not</i> a general-purpose {@code Map}43* implementation! While this class implements the {@code Map} interface, it44* intentionally violates {@code Map's} general contract, which mandates the45* use of the {@code equals} method when comparing objects. This class is46* designed for use only in the rare cases wherein reference-equality47* semantics are required.</b>48*49* <p>A typical use of this class is <i>topology-preserving object graph50* transformations</i>, such as serialization or deep-copying. To perform such51* a transformation, a program must maintain a "node table" that keeps track52* of all the object references that have already been processed. The node53* table must not equate distinct objects even if they happen to be equal.54* Another typical use of this class is to maintain <i>proxy objects</i>. For55* example, a debugging facility might wish to maintain a proxy object for56* each object in the program being debugged.57*58* <p>This class provides all of the optional map operations, and permits59* {@code null} values and the {@code null} key. This class makes no60* guarantees as to the order of the map; in particular, it does not guarantee61* that the order will remain constant over time.62*63* <p>This class provides constant-time performance for the basic64* operations ({@code get} and {@code put}), assuming the system65* identity hash function ({@link System#identityHashCode(Object)})66* disperses elements properly among the buckets.67*68* <p>This class has one tuning parameter (which affects performance but not69* semantics): <i>expected maximum size</i>. This parameter is the maximum70* number of key-value mappings that the map is expected to hold. Internally,71* this parameter is used to determine the number of buckets initially72* comprising the hash table. The precise relationship between the expected73* maximum size and the number of buckets is unspecified.74*75* <p>If the size of the map (the number of key-value mappings) sufficiently76* exceeds the expected maximum size, the number of buckets is increased.77* Increasing the number of buckets ("rehashing") may be fairly expensive, so78* it pays to create identity hash maps with a sufficiently large expected79* maximum size. On the other hand, iteration over collection views requires80* time proportional to the number of buckets in the hash table, so it81* pays not to set the expected maximum size too high if you are especially82* concerned with iteration performance or memory usage.83*84* <p><strong>Note that this implementation is not synchronized.</strong>85* If multiple threads access an identity hash map concurrently, and at86* least one of the threads modifies the map structurally, it <i>must</i>87* be synchronized externally. (A structural modification is any operation88* that adds or deletes one or more mappings; merely changing the value89* associated with a key that an instance already contains is not a90* structural modification.) This is typically accomplished by91* synchronizing on some object that naturally encapsulates the map.92*93* If no such object exists, the map should be "wrapped" using the94* {@link Collections#synchronizedMap Collections.synchronizedMap}95* method. This is best done at creation time, to prevent accidental96* unsynchronized access to the map:<pre>97* Map m = Collections.synchronizedMap(new IdentityHashMap(...));</pre>98*99* <p>The iterators returned by the {@code iterator} method of the100* collections returned by all of this class's "collection view101* methods" are <i>fail-fast</i>: if the map is structurally modified102* at any time after the iterator is created, in any way except103* through the iterator's own {@code remove} method, the iterator104* will throw a {@link ConcurrentModificationException}. Thus, in the105* face of concurrent modification, the iterator fails quickly and106* cleanly, rather than risking arbitrary, non-deterministic behavior107* at an undetermined time in the future.108*109* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed110* as it is, generally speaking, impossible to make any hard guarantees in the111* presence of unsynchronized concurrent modification. Fail-fast iterators112* throw {@code ConcurrentModificationException} on a best-effort basis.113* Therefore, it would be wrong to write a program that depended on this114* exception for its correctness: <i>fail-fast iterators should be used only115* to detect bugs.</i>116*117* <p>This class is a member of the118* <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">119* Java Collections Framework</a>.120*121* @implNote122* <p>This is a simple <i>linear-probe</i> hash table,123* as described for example in texts by Sedgewick and Knuth. The array124* contains alternating keys and values, with keys at even indexes and values125* at odd indexes. (This arrangement has better locality for large126* tables than does using separate arrays.) For many Java implementations127* and operation mixes, this class will yield better performance than128* {@link HashMap}, which uses <i>chaining</i> rather than linear-probing.129*130* @see System#identityHashCode(Object)131* @see Object#hashCode()132* @see Collection133* @see Map134* @see HashMap135* @see TreeMap136* @author Doug Lea and Josh Bloch137* @since 1.4138*/139140public class IdentityHashMap<K,V>141extends AbstractMap<K,V>142implements Map<K,V>, java.io.Serializable, Cloneable143{144/**145* The initial capacity used by the no-args constructor.146* MUST be a power of two. The value 32 corresponds to the147* (specified) expected maximum size of 21, given a load factor148* of 2/3.149*/150private static final int DEFAULT_CAPACITY = 32;151152/**153* The minimum capacity, used if a lower value is implicitly specified154* by either of the constructors with arguments. The value 4 corresponds155* to an expected maximum size of 2, given a load factor of 2/3.156* MUST be a power of two.157*/158private static final int MINIMUM_CAPACITY = 4;159160/**161* The maximum capacity, used if a higher value is implicitly specified162* by either of the constructors with arguments.163* MUST be a power of two <= 1<<29.164*165* In fact, the map can hold no more than MAXIMUM_CAPACITY-1 items166* because it has to have at least one slot with the key == null167* in order to avoid infinite loops in get(), put(), remove()168*/169private static final int MAXIMUM_CAPACITY = 1 << 29;170171/**172* The table, resized as necessary. Length MUST always be a power of two.173*/174transient Object[] table; // non-private to simplify nested class access175176/**177* The number of key-value mappings contained in this identity hash map.178*179* @serial180*/181int size;182183/**184* The number of modifications, to support fast-fail iterators185*/186transient int modCount;187188/**189* Value representing null keys inside tables.190*/191static final Object NULL_KEY = new Object();192193/**194* Use NULL_KEY for key if it is null.195*/196private static Object maskNull(Object key) {197return (key == null ? NULL_KEY : key);198}199200/**201* Returns internal representation of null key back to caller as null.202*/203static final Object unmaskNull(Object key) {204return (key == NULL_KEY ? null : key);205}206207/**208* Constructs a new, empty identity hash map with a default expected209* maximum size (21).210*/211public IdentityHashMap() {212init(DEFAULT_CAPACITY);213}214215/**216* Constructs a new, empty map with the specified expected maximum size.217* Putting more than the expected number of key-value mappings into218* the map may cause the internal data structure to grow, which may be219* somewhat time-consuming.220*221* @param expectedMaxSize the expected maximum size of the map222* @throws IllegalArgumentException if {@code expectedMaxSize} is negative223*/224public IdentityHashMap(int expectedMaxSize) {225if (expectedMaxSize < 0)226throw new IllegalArgumentException("expectedMaxSize is negative: "227+ expectedMaxSize);228init(capacity(expectedMaxSize));229}230231/**232* Returns the appropriate capacity for the given expected maximum size.233* Returns the smallest power of two between MINIMUM_CAPACITY and234* MAXIMUM_CAPACITY, inclusive, that is greater than (3 *235* expectedMaxSize)/2, if such a number exists. Otherwise returns236* MAXIMUM_CAPACITY.237*/238private static int capacity(int expectedMaxSize) {239// assert expectedMaxSize >= 0;240return241(expectedMaxSize > MAXIMUM_CAPACITY / 3) ? MAXIMUM_CAPACITY :242(expectedMaxSize <= 2 * MINIMUM_CAPACITY / 3) ? MINIMUM_CAPACITY :243Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1));244}245246/**247* Initializes object to be an empty map with the specified initial248* capacity, which is assumed to be a power of two between249* MINIMUM_CAPACITY and MAXIMUM_CAPACITY inclusive.250*/251private void init(int initCapacity) {252// assert (initCapacity & -initCapacity) == initCapacity; // power of 2253// assert initCapacity >= MINIMUM_CAPACITY;254// assert initCapacity <= MAXIMUM_CAPACITY;255256table = new Object[2 * initCapacity];257}258259/**260* Constructs a new identity hash map containing the keys-value mappings261* in the specified map.262*263* @param m the map whose mappings are to be placed into this map264* @throws NullPointerException if the specified map is null265*/266public IdentityHashMap(Map<? extends K, ? extends V> m) {267// Allow for a bit of growth268this((int) ((1 + m.size()) * 1.1));269putAll(m);270}271272/**273* Returns the number of key-value mappings in this identity hash map.274*275* @return the number of key-value mappings in this map276*/277public int size() {278return size;279}280281/**282* Returns {@code true} if this identity hash map contains no key-value283* mappings.284*285* @return {@code true} if this identity hash map contains no key-value286* mappings287*/288public boolean isEmpty() {289return size == 0;290}291292/**293* Returns index for Object x.294*/295private static int hash(Object x, int length) {296int h = System.identityHashCode(x);297// Multiply by -254 to use the hash LSB and to ensure index is even298return ((h << 1) - (h << 8)) & (length - 1);299}300301/**302* Circularly traverses table of size len.303*/304private static int nextKeyIndex(int i, int len) {305return (i + 2 < len ? i + 2 : 0);306}307308/**309* Returns the value to which the specified key is mapped,310* or {@code null} if this map contains no mapping for the key.311*312* <p>More formally, if this map contains a mapping from a key313* {@code k} to a value {@code v} such that {@code (key == k)},314* then this method returns {@code v}; otherwise it returns315* {@code null}. (There can be at most one such mapping.)316*317* <p>A return value of {@code null} does not <i>necessarily</i>318* indicate that the map contains no mapping for the key; it's also319* possible that the map explicitly maps the key to {@code null}.320* The {@link #containsKey containsKey} operation may be used to321* distinguish these two cases.322*323* @see #put(Object, Object)324*/325@SuppressWarnings("unchecked")326public V get(Object key) {327Object k = maskNull(key);328Object[] tab = table;329int len = tab.length;330int i = hash(k, len);331while (true) {332Object item = tab[i];333if (item == k)334return (V) tab[i + 1];335if (item == null)336return null;337i = nextKeyIndex(i, len);338}339}340341/**342* Tests whether the specified object reference is a key in this identity343* hash map.344*345* @param key possible key346* @return {@code true} if the specified object reference is a key347* in this map348* @see #containsValue(Object)349*/350public boolean containsKey(Object key) {351Object k = maskNull(key);352Object[] tab = table;353int len = tab.length;354int i = hash(k, len);355while (true) {356Object item = tab[i];357if (item == k)358return true;359if (item == null)360return false;361i = nextKeyIndex(i, len);362}363}364365/**366* Tests whether the specified object reference is a value in this identity367* hash map.368*369* @param value value whose presence in this map is to be tested370* @return {@code true} if this map maps one or more keys to the371* specified object reference372* @see #containsKey(Object)373*/374public boolean containsValue(Object value) {375Object[] tab = table;376for (int i = 1; i < tab.length; i += 2)377if (tab[i] == value && tab[i - 1] != null)378return true;379380return false;381}382383/**384* Tests if the specified key-value mapping is in the map.385*386* @param key possible key387* @param value possible value388* @return {@code true} if and only if the specified key-value389* mapping is in the map390*/391private boolean containsMapping(Object key, Object value) {392Object k = maskNull(key);393Object[] tab = table;394int len = tab.length;395int i = hash(k, len);396while (true) {397Object item = tab[i];398if (item == k)399return tab[i + 1] == value;400if (item == null)401return false;402i = nextKeyIndex(i, len);403}404}405406/**407* Associates the specified value with the specified key in this identity408* hash map. If the map previously contained a mapping for the key, the409* old value is replaced.410*411* @param key the key with which the specified value is to be associated412* @param value the value to be associated with the specified key413* @return the previous value associated with {@code key}, or414* {@code null} if there was no mapping for {@code key}.415* (A {@code null} return can also indicate that the map416* previously associated {@code null} with {@code key}.)417* @see Object#equals(Object)418* @see #get(Object)419* @see #containsKey(Object)420*/421public V put(K key, V value) {422final Object k = maskNull(key);423424retryAfterResize: for (;;) {425final Object[] tab = table;426final int len = tab.length;427int i = hash(k, len);428429for (Object item; (item = tab[i]) != null;430i = nextKeyIndex(i, len)) {431if (item == k) {432@SuppressWarnings("unchecked")433V oldValue = (V) tab[i + 1];434tab[i + 1] = value;435return oldValue;436}437}438439final int s = size + 1;440// Use optimized form of 3 * s.441// Next capacity is len, 2 * current capacity.442if (s + (s << 1) > len && resize(len))443continue retryAfterResize;444445modCount++;446tab[i] = k;447tab[i + 1] = value;448size = s;449return null;450}451}452453/**454* Resizes the table if necessary to hold given capacity.455*456* @param newCapacity the new capacity, must be a power of two.457* @return whether a resize did in fact take place458*/459private boolean resize(int newCapacity) {460// assert (newCapacity & -newCapacity) == newCapacity; // power of 2461int newLength = newCapacity * 2;462463Object[] oldTable = table;464int oldLength = oldTable.length;465if (oldLength == 2 * MAXIMUM_CAPACITY) { // can't expand any further466if (size == MAXIMUM_CAPACITY - 1)467throw new IllegalStateException("Capacity exhausted.");468return false;469}470if (oldLength >= newLength)471return false;472473Object[] newTable = new Object[newLength];474475for (int j = 0; j < oldLength; j += 2) {476Object key = oldTable[j];477if (key != null) {478Object value = oldTable[j+1];479oldTable[j] = null;480oldTable[j+1] = null;481int i = hash(key, newLength);482while (newTable[i] != null)483i = nextKeyIndex(i, newLength);484newTable[i] = key;485newTable[i + 1] = value;486}487}488table = newTable;489return true;490}491492/**493* Copies all of the mappings from the specified map to this map.494* These mappings will replace any mappings that this map had for495* any of the keys currently in the specified map.496*497* @param m mappings to be stored in this map498* @throws NullPointerException if the specified map is null499*/500public void putAll(Map<? extends K, ? extends V> m) {501int n = m.size();502if (n == 0)503return;504if (n > size)505resize(capacity(n)); // conservatively pre-expand506507for (Entry<? extends K, ? extends V> e : m.entrySet())508put(e.getKey(), e.getValue());509}510511/**512* Removes the mapping for this key from this map if present.513*514* @param key key whose mapping is to be removed from the map515* @return the previous value associated with {@code key}, or516* {@code null} if there was no mapping for {@code key}.517* (A {@code null} return can also indicate that the map518* previously associated {@code null} with {@code key}.)519*/520public V remove(Object key) {521Object k = maskNull(key);522Object[] tab = table;523int len = tab.length;524int i = hash(k, len);525526while (true) {527Object item = tab[i];528if (item == k) {529modCount++;530size--;531@SuppressWarnings("unchecked")532V oldValue = (V) tab[i + 1];533tab[i + 1] = null;534tab[i] = null;535closeDeletion(i);536return oldValue;537}538if (item == null)539return null;540i = nextKeyIndex(i, len);541}542}543544/**545* Removes the specified key-value mapping from the map if it is present.546*547* @param key possible key548* @param value possible value549* @return {@code true} if and only if the specified key-value550* mapping was in the map551*/552private boolean removeMapping(Object key, Object value) {553Object k = maskNull(key);554Object[] tab = table;555int len = tab.length;556int i = hash(k, len);557558while (true) {559Object item = tab[i];560if (item == k) {561if (tab[i + 1] != value)562return false;563modCount++;564size--;565tab[i] = null;566tab[i + 1] = null;567closeDeletion(i);568return true;569}570if (item == null)571return false;572i = nextKeyIndex(i, len);573}574}575576/**577* Rehash all possibly-colliding entries following a578* deletion. This preserves the linear-probe579* collision properties required by get, put, etc.580*581* @param d the index of a newly empty deleted slot582*/583private void closeDeletion(int d) {584// Adapted from Knuth Section 6.4 Algorithm R585Object[] tab = table;586int len = tab.length;587588// Look for items to swap into newly vacated slot589// starting at index immediately following deletion,590// and continuing until a null slot is seen, indicating591// the end of a run of possibly-colliding keys.592Object item;593for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;594i = nextKeyIndex(i, len) ) {595// The following test triggers if the item at slot i (which596// hashes to be at slot r) should take the spot vacated by d.597// If so, we swap it in, and then continue with d now at the598// newly vacated i. This process will terminate when we hit599// the null slot at the end of this run.600// The test is messy because we are using a circular table.601int r = hash(item, len);602if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {603tab[d] = item;604tab[d + 1] = tab[i + 1];605tab[i] = null;606tab[i + 1] = null;607d = i;608}609}610}611612/**613* Removes all of the mappings from this map.614* The map will be empty after this call returns.615*/616public void clear() {617modCount++;618Object[] tab = table;619for (int i = 0; i < tab.length; i++)620tab[i] = null;621size = 0;622}623624/**625* Compares the specified object with this map for equality. Returns626* {@code true} if the given object is also a map and the two maps627* represent identical object-reference mappings. More formally, this628* map is equal to another map {@code m} if and only if629* {@code this.entrySet().equals(m.entrySet())}.630*631* <p><b>Owing to the reference-equality-based semantics of this map it is632* possible that the symmetry and transitivity requirements of the633* {@code Object.equals} contract may be violated if this map is compared634* to a normal map. However, the {@code Object.equals} contract is635* guaranteed to hold among {@code IdentityHashMap} instances.</b>636*637* @param o object to be compared for equality with this map638* @return {@code true} if the specified object is equal to this map639* @see Object#equals(Object)640*/641public boolean equals(Object o) {642if (o == this) {643return true;644} else if (o instanceof IdentityHashMap<?, ?> m) {645if (m.size() != size)646return false;647648Object[] tab = m.table;649for (int i = 0; i < tab.length; i+=2) {650Object k = tab[i];651if (k != null && !containsMapping(k, tab[i + 1]))652return false;653}654return true;655} else if (o instanceof Map<?, ?> m) {656return entrySet().equals(m.entrySet());657} else {658return false; // o is not a Map659}660}661662/**663* Returns the hash code value for this map. The hash code of a map is664* defined to be the sum of the hash codes of each entry in the map's665* {@code entrySet()} view. This ensures that {@code m1.equals(m2)}666* implies that {@code m1.hashCode()==m2.hashCode()} for any two667* {@code IdentityHashMap} instances {@code m1} and {@code m2}, as668* required by the general contract of {@link Object#hashCode}.669*670* <p><b>Owing to the reference-equality-based semantics of the671* {@code Map.Entry} instances in the set returned by this map's672* {@code entrySet} method, it is possible that the contractual673* requirement of {@code Object.hashCode} mentioned in the previous674* paragraph will be violated if one of the two objects being compared is675* an {@code IdentityHashMap} instance and the other is a normal map.</b>676*677* @return the hash code value for this map678* @see Object#equals(Object)679* @see #equals(Object)680*/681public int hashCode() {682int result = 0;683Object[] tab = table;684for (int i = 0; i < tab.length; i +=2) {685Object key = tab[i];686if (key != null) {687Object k = unmaskNull(key);688result += System.identityHashCode(k) ^689System.identityHashCode(tab[i + 1]);690}691}692return result;693}694695/**696* Returns a shallow copy of this identity hash map: the keys and values697* themselves are not cloned.698*699* @return a shallow copy of this map700*/701public Object clone() {702try {703IdentityHashMap<?,?> m = (IdentityHashMap<?,?>) super.clone();704m.entrySet = null;705m.table = table.clone();706return m;707} catch (CloneNotSupportedException e) {708throw new InternalError(e);709}710}711712private abstract class IdentityHashMapIterator<T> implements Iterator<T> {713int index = (size != 0 ? 0 : table.length); // current slot.714int expectedModCount = modCount; // to support fast-fail715int lastReturnedIndex = -1; // to allow remove()716boolean indexValid; // To avoid unnecessary next computation717Object[] traversalTable = table; // reference to main table or copy718719public boolean hasNext() {720Object[] tab = traversalTable;721for (int i = index; i < tab.length; i+=2) {722Object key = tab[i];723if (key != null) {724index = i;725return indexValid = true;726}727}728index = tab.length;729return false;730}731732protected int nextIndex() {733if (modCount != expectedModCount)734throw new ConcurrentModificationException();735if (!indexValid && !hasNext())736throw new NoSuchElementException();737738indexValid = false;739lastReturnedIndex = index;740index += 2;741return lastReturnedIndex;742}743744public void remove() {745if (lastReturnedIndex == -1)746throw new IllegalStateException();747if (modCount != expectedModCount)748throw new ConcurrentModificationException();749750expectedModCount = ++modCount;751int deletedSlot = lastReturnedIndex;752lastReturnedIndex = -1;753// back up index to revisit new contents after deletion754index = deletedSlot;755indexValid = false;756757// Removal code proceeds as in closeDeletion except that758// it must catch the rare case where an element already759// seen is swapped into a vacant slot that will be later760// traversed by this iterator. We cannot allow future761// next() calls to return it again. The likelihood of762// this occurring under 2/3 load factor is very slim, but763// when it does happen, we must make a copy of the rest of764// the table to use for the rest of the traversal. Since765// this can only happen when we are near the end of the table,766// even in these rare cases, this is not very expensive in767// time or space.768769Object[] tab = traversalTable;770int len = tab.length;771772int d = deletedSlot;773Object key = tab[d];774tab[d] = null; // vacate the slot775tab[d + 1] = null;776777// If traversing a copy, remove in real table.778// We can skip gap-closure on copy.779if (tab != IdentityHashMap.this.table) {780IdentityHashMap.this.remove(key);781expectedModCount = modCount;782return;783}784785size--;786787Object item;788for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;789i = nextKeyIndex(i, len)) {790int r = hash(item, len);791// See closeDeletion for explanation of this conditional792if ((i < r && (r <= d || d <= i)) ||793(r <= d && d <= i)) {794795// If we are about to swap an already-seen element796// into a slot that may later be returned by next(),797// then clone the rest of table for use in future798// next() calls. It is OK that our copy will have799// a gap in the "wrong" place, since it will never800// be used for searching anyway.801802if (i < deletedSlot && d >= deletedSlot &&803traversalTable == IdentityHashMap.this.table) {804int remaining = len - deletedSlot;805Object[] newTable = new Object[remaining];806System.arraycopy(tab, deletedSlot,807newTable, 0, remaining);808traversalTable = newTable;809index = 0;810}811812tab[d] = item;813tab[d + 1] = tab[i + 1];814tab[i] = null;815tab[i + 1] = null;816d = i;817}818}819}820}821822private class KeyIterator extends IdentityHashMapIterator<K> {823@SuppressWarnings("unchecked")824public K next() {825return (K) unmaskNull(traversalTable[nextIndex()]);826}827}828829private class ValueIterator extends IdentityHashMapIterator<V> {830@SuppressWarnings("unchecked")831public V next() {832return (V) traversalTable[nextIndex() + 1];833}834}835836private class EntryIterator837extends IdentityHashMapIterator<Map.Entry<K,V>>838{839private Entry lastReturnedEntry;840841public Map.Entry<K,V> next() {842lastReturnedEntry = new Entry(nextIndex());843return lastReturnedEntry;844}845846public void remove() {847lastReturnedIndex =848((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);849super.remove();850lastReturnedEntry.index = lastReturnedIndex;851lastReturnedEntry = null;852}853854private class Entry implements Map.Entry<K,V> {855private int index;856857private Entry(int index) {858this.index = index;859}860861@SuppressWarnings("unchecked")862public K getKey() {863checkIndexForEntryUse();864return (K) unmaskNull(traversalTable[index]);865}866867@SuppressWarnings("unchecked")868public V getValue() {869checkIndexForEntryUse();870return (V) traversalTable[index+1];871}872873@SuppressWarnings("unchecked")874public V setValue(V value) {875checkIndexForEntryUse();876V oldValue = (V) traversalTable[index+1];877traversalTable[index+1] = value;878// if shadowing, force into main table879if (traversalTable != IdentityHashMap.this.table)880put((K) traversalTable[index], value);881return oldValue;882}883884public boolean equals(Object o) {885if (index < 0)886return super.equals(o);887888return o instanceof Map.Entry<?, ?> e889&& e.getKey() == unmaskNull(traversalTable[index])890&& e.getValue() == traversalTable[index+1];891}892893public int hashCode() {894if (lastReturnedIndex < 0)895return super.hashCode();896897return (System.identityHashCode(unmaskNull(traversalTable[index])) ^898System.identityHashCode(traversalTable[index+1]));899}900901public String toString() {902if (index < 0)903return super.toString();904905return (unmaskNull(traversalTable[index]) + "="906+ traversalTable[index+1]);907}908909private void checkIndexForEntryUse() {910if (index < 0)911throw new IllegalStateException("Entry was removed");912}913}914}915916// Views917918/**919* This field is initialized to contain an instance of the entry set920* view the first time this view is requested. The view is stateless,921* so there's no reason to create more than one.922*/923private transient Set<Map.Entry<K,V>> entrySet;924925/**926* Returns an identity-based set view of the keys contained in this map.927* The set is backed by the map, so changes to the map are reflected in928* the set, and vice-versa. If the map is modified while an iteration929* over the set is in progress, the results of the iteration are930* undefined. The set supports element removal, which removes the931* corresponding mapping from the map, via the {@code Iterator.remove},932* {@code Set.remove}, {@code removeAll}, {@code retainAll}, and933* {@code clear} methods. It does not support the {@code add} or934* {@code addAll} methods.935*936* <p><b>While the object returned by this method implements the937* {@code Set} interface, it does <i>not</i> obey {@code Set's} general938* contract. Like its backing map, the set returned by this method939* defines element equality as reference-equality rather than940* object-equality. This affects the behavior of its {@code contains},941* {@code remove}, {@code containsAll}, {@code equals}, and942* {@code hashCode} methods.</b>943*944* <p><b>The {@code equals} method of the returned set returns {@code true}945* only if the specified object is a set containing exactly the same946* object references as the returned set. The symmetry and transitivity947* requirements of the {@code Object.equals} contract may be violated if948* the set returned by this method is compared to a normal set. However,949* the {@code Object.equals} contract is guaranteed to hold among sets950* returned by this method.</b>951*952* <p>The {@code hashCode} method of the returned set returns the sum of953* the <i>identity hashcodes</i> of the elements in the set, rather than954* the sum of their hashcodes. This is mandated by the change in the955* semantics of the {@code equals} method, in order to enforce the956* general contract of the {@code Object.hashCode} method among sets957* returned by this method.958*959* @return an identity-based set view of the keys contained in this map960* @see Object#equals(Object)961* @see System#identityHashCode(Object)962*/963public Set<K> keySet() {964Set<K> ks = keySet;965if (ks == null) {966ks = new KeySet();967keySet = ks;968}969return ks;970}971972private class KeySet extends AbstractSet<K> {973public Iterator<K> iterator() {974return new KeyIterator();975}976public int size() {977return size;978}979public boolean contains(Object o) {980return containsKey(o);981}982public boolean remove(Object o) {983int oldSize = size;984IdentityHashMap.this.remove(o);985return size != oldSize;986}987/*988* Must revert from AbstractSet's impl to AbstractCollection's, as989* the former contains an optimization that results in incorrect990* behavior when c is a smaller "normal" (non-identity-based) Set.991*/992public boolean removeAll(Collection<?> c) {993Objects.requireNonNull(c);994boolean modified = false;995for (Iterator<K> i = iterator(); i.hasNext(); ) {996if (c.contains(i.next())) {997i.remove();998modified = true;999}1000}1001return modified;1002}1003public void clear() {1004IdentityHashMap.this.clear();1005}1006public int hashCode() {1007int result = 0;1008for (K key : this)1009result += System.identityHashCode(key);1010return result;1011}1012public Object[] toArray() {1013return toArray(new Object[0]);1014}1015@SuppressWarnings("unchecked")1016public <T> T[] toArray(T[] a) {1017int expectedModCount = modCount;1018int size = size();1019if (a.length < size)1020a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);1021Object[] tab = table;1022int ti = 0;1023for (int si = 0; si < tab.length; si += 2) {1024Object key;1025if ((key = tab[si]) != null) { // key present ?1026// more elements than expected -> concurrent modification from other thread1027if (ti >= size) {1028throw new ConcurrentModificationException();1029}1030a[ti++] = (T) unmaskNull(key); // unmask key1031}1032}1033// fewer elements than expected or concurrent modification from other thread detected1034if (ti < size || expectedModCount != modCount) {1035throw new ConcurrentModificationException();1036}1037// final null marker as per spec1038if (ti < a.length) {1039a[ti] = null;1040}1041return a;1042}10431044public Spliterator<K> spliterator() {1045return new KeySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);1046}1047}10481049/**1050* Returns a {@link Collection} view of the values contained in this map.1051* The collection is backed by the map, so changes to the map are1052* reflected in the collection, and vice-versa. If the map is1053* modified while an iteration over the collection is in progress,1054* the results of the iteration are undefined. The collection1055* supports element removal, which removes the corresponding1056* mapping from the map, via the {@code Iterator.remove},1057* {@code Collection.remove}, {@code removeAll},1058* {@code retainAll} and {@code clear} methods. It does not1059* support the {@code add} or {@code addAll} methods.1060*1061* <p><b>While the object returned by this method implements the1062* {@code Collection} interface, it does <i>not</i> obey1063* {@code Collection's} general contract. Like its backing map,1064* the collection returned by this method defines element equality as1065* reference-equality rather than object-equality. This affects the1066* behavior of its {@code contains}, {@code remove} and1067* {@code containsAll} methods.</b>1068*/1069public Collection<V> values() {1070Collection<V> vs = values;1071if (vs == null) {1072vs = new Values();1073values = vs;1074}1075return vs;1076}10771078private class Values extends AbstractCollection<V> {1079public Iterator<V> iterator() {1080return new ValueIterator();1081}1082public int size() {1083return size;1084}1085public boolean contains(Object o) {1086return containsValue(o);1087}1088public boolean remove(Object o) {1089for (Iterator<V> i = iterator(); i.hasNext(); ) {1090if (i.next() == o) {1091i.remove();1092return true;1093}1094}1095return false;1096}1097public void clear() {1098IdentityHashMap.this.clear();1099}1100public Object[] toArray() {1101return toArray(new Object[0]);1102}1103@SuppressWarnings("unchecked")1104public <T> T[] toArray(T[] a) {1105int expectedModCount = modCount;1106int size = size();1107if (a.length < size)1108a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);1109Object[] tab = table;1110int ti = 0;1111for (int si = 0; si < tab.length; si += 2) {1112if (tab[si] != null) { // key present ?1113// more elements than expected -> concurrent modification from other thread1114if (ti >= size) {1115throw new ConcurrentModificationException();1116}1117a[ti++] = (T) tab[si+1]; // copy value1118}1119}1120// fewer elements than expected or concurrent modification from other thread detected1121if (ti < size || expectedModCount != modCount) {1122throw new ConcurrentModificationException();1123}1124// final null marker as per spec1125if (ti < a.length) {1126a[ti] = null;1127}1128return a;1129}11301131public Spliterator<V> spliterator() {1132return new ValueSpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);1133}1134}11351136/**1137* Returns a {@link Set} view of the mappings contained in this map.1138* Each element in the returned set is a reference-equality-based1139* {@code Map.Entry}. The set is backed by the map, so changes1140* to the map are reflected in the set, and vice-versa. If the1141* map is modified while an iteration over the set is in progress,1142* the results of the iteration are undefined. The set supports1143* element removal, which removes the corresponding mapping from1144* the map, via the {@code Iterator.remove}, {@code Set.remove},1145* {@code removeAll}, {@code retainAll} and {@code clear}1146* methods. It does not support the {@code add} or1147* {@code addAll} methods.1148*1149* <p>Like the backing map, the {@code Map.Entry} objects in the set1150* returned by this method define key and value equality as1151* reference-equality rather than object-equality. This affects the1152* behavior of the {@code equals} and {@code hashCode} methods of these1153* {@code Map.Entry} objects. A reference-equality based {@code Map.Entry1154* e} is equal to an object {@code o} if and only if {@code o} is a1155* {@code Map.Entry} and {@code e.getKey()==o.getKey() &&1156* e.getValue()==o.getValue()}. To accommodate these equals1157* semantics, the {@code hashCode} method returns1158* {@code System.identityHashCode(e.getKey()) ^1159* System.identityHashCode(e.getValue())}.1160*1161* <p><b>Owing to the reference-equality-based semantics of the1162* {@code Map.Entry} instances in the set returned by this method,1163* it is possible that the symmetry and transitivity requirements of1164* the {@link Object#equals(Object)} contract may be violated if any of1165* the entries in the set is compared to a normal map entry, or if1166* the set returned by this method is compared to a set of normal map1167* entries (such as would be returned by a call to this method on a normal1168* map). However, the {@code Object.equals} contract is guaranteed to1169* hold among identity-based map entries, and among sets of such entries.1170* </b>1171*1172* @return a set view of the identity-mappings contained in this map1173*/1174public Set<Map.Entry<K,V>> entrySet() {1175Set<Map.Entry<K,V>> es = entrySet;1176if (es != null)1177return es;1178else1179return entrySet = new EntrySet();1180}11811182private class EntrySet extends AbstractSet<Map.Entry<K,V>> {1183public Iterator<Map.Entry<K,V>> iterator() {1184return new EntryIterator();1185}1186public boolean contains(Object o) {1187return o instanceof Entry<?, ?> entry1188&& containsMapping(entry.getKey(), entry.getValue());1189}1190public boolean remove(Object o) {1191return o instanceof Entry<?, ?> entry1192&& removeMapping(entry.getKey(), entry.getValue());1193}1194public int size() {1195return size;1196}1197public void clear() {1198IdentityHashMap.this.clear();1199}1200/*1201* Must revert from AbstractSet's impl to AbstractCollection's, as1202* the former contains an optimization that results in incorrect1203* behavior when c is a smaller "normal" (non-identity-based) Set.1204*/1205public boolean removeAll(Collection<?> c) {1206Objects.requireNonNull(c);1207boolean modified = false;1208for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) {1209if (c.contains(i.next())) {1210i.remove();1211modified = true;1212}1213}1214return modified;1215}12161217public Object[] toArray() {1218return toArray(new Object[0]);1219}12201221@SuppressWarnings("unchecked")1222public <T> T[] toArray(T[] a) {1223int expectedModCount = modCount;1224int size = size();1225if (a.length < size)1226a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);1227Object[] tab = table;1228int ti = 0;1229for (int si = 0; si < tab.length; si += 2) {1230Object key;1231if ((key = tab[si]) != null) { // key present ?1232// more elements than expected -> concurrent modification from other thread1233if (ti >= size) {1234throw new ConcurrentModificationException();1235}1236a[ti++] = (T) new AbstractMap.SimpleEntry<>(unmaskNull(key), tab[si + 1]);1237}1238}1239// fewer elements than expected or concurrent modification from other thread detected1240if (ti < size || expectedModCount != modCount) {1241throw new ConcurrentModificationException();1242}1243// final null marker as per spec1244if (ti < a.length) {1245a[ti] = null;1246}1247return a;1248}12491250public Spliterator<Map.Entry<K,V>> spliterator() {1251return new EntrySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);1252}1253}12541255@java.io.Serial1256private static final long serialVersionUID = 8188218128353913216L;12571258/**1259* Saves the state of the {@code IdentityHashMap} instance to a stream1260* (i.e., serializes it).1261*1262* @serialData The <i>size</i> of the HashMap (the number of key-value1263* mappings) ({@code int}), followed by the key (Object) and1264* value (Object) for each key-value mapping represented by the1265* IdentityHashMap. The key-value mappings are emitted in no1266* particular order.1267*/1268@java.io.Serial1269private void writeObject(java.io.ObjectOutputStream s)1270throws java.io.IOException {1271// Write out and any hidden stuff1272s.defaultWriteObject();12731274// Write out size (number of Mappings)1275s.writeInt(size);12761277// Write out keys and values (alternating)1278Object[] tab = table;1279for (int i = 0; i < tab.length; i += 2) {1280Object key = tab[i];1281if (key != null) {1282s.writeObject(unmaskNull(key));1283s.writeObject(tab[i + 1]);1284}1285}1286}12871288/**1289* Reconstitutes the {@code IdentityHashMap} instance from a stream (i.e.,1290* deserializes it).1291*/1292@java.io.Serial1293private void readObject(java.io.ObjectInputStream s)1294throws java.io.IOException, ClassNotFoundException {1295// Read in any hidden stuff1296s.defaultReadObject();12971298// Read in size (number of Mappings)1299int size = s.readInt();1300if (size < 0)1301throw new java.io.StreamCorruptedException1302("Illegal mappings count: " + size);1303int cap = capacity(size);1304SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, cap);1305init(cap);13061307// Read the keys and values, and put the mappings in the table1308for (int i=0; i<size; i++) {1309@SuppressWarnings("unchecked")1310K key = (K) s.readObject();1311@SuppressWarnings("unchecked")1312V value = (V) s.readObject();1313putForCreate(key, value);1314}1315}13161317/**1318* The put method for readObject. It does not resize the table,1319* update modCount, etc.1320*/1321private void putForCreate(K key, V value)1322throws java.io.StreamCorruptedException1323{1324Object k = maskNull(key);1325Object[] tab = table;1326int len = tab.length;1327int i = hash(k, len);13281329Object item;1330while ( (item = tab[i]) != null) {1331if (item == k)1332throw new java.io.StreamCorruptedException();1333i = nextKeyIndex(i, len);1334}1335tab[i] = k;1336tab[i + 1] = value;1337}13381339@SuppressWarnings("unchecked")1340@Override1341public void forEach(BiConsumer<? super K, ? super V> action) {1342Objects.requireNonNull(action);1343int expectedModCount = modCount;13441345Object[] t = table;1346for (int index = 0; index < t.length; index += 2) {1347Object k = t[index];1348if (k != null) {1349action.accept((K) unmaskNull(k), (V) t[index + 1]);1350}13511352if (modCount != expectedModCount) {1353throw new ConcurrentModificationException();1354}1355}1356}13571358@SuppressWarnings("unchecked")1359@Override1360public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {1361Objects.requireNonNull(function);1362int expectedModCount = modCount;13631364Object[] t = table;1365for (int index = 0; index < t.length; index += 2) {1366Object k = t[index];1367if (k != null) {1368t[index + 1] = function.apply((K) unmaskNull(k), (V) t[index + 1]);1369}13701371if (modCount != expectedModCount) {1372throw new ConcurrentModificationException();1373}1374}1375}13761377/**1378* Similar form as array-based Spliterators, but skips blank elements,1379* and guestimates size as decreasing by half per split.1380*/1381static class IdentityHashMapSpliterator<K,V> {1382final IdentityHashMap<K,V> map;1383int index; // current index, modified on advance/split1384int fence; // -1 until first use; then one past last index1385int est; // size estimate1386int expectedModCount; // initialized when fence set13871388IdentityHashMapSpliterator(IdentityHashMap<K,V> map, int origin,1389int fence, int est, int expectedModCount) {1390this.map = map;1391this.index = origin;1392this.fence = fence;1393this.est = est;1394this.expectedModCount = expectedModCount;1395}13961397final int getFence() { // initialize fence and size on first use1398int hi;1399if ((hi = fence) < 0) {1400est = map.size;1401expectedModCount = map.modCount;1402hi = fence = map.table.length;1403}1404return hi;1405}14061407public final long estimateSize() {1408getFence(); // force init1409return (long) est;1410}1411}14121413static final class KeySpliterator<K,V>1414extends IdentityHashMapSpliterator<K,V>1415implements Spliterator<K> {1416KeySpliterator(IdentityHashMap<K,V> map, int origin, int fence, int est,1417int expectedModCount) {1418super(map, origin, fence, est, expectedModCount);1419}14201421public KeySpliterator<K,V> trySplit() {1422int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;1423return (lo >= mid) ? null :1424new KeySpliterator<>(map, lo, index = mid, est >>>= 1,1425expectedModCount);1426}14271428@SuppressWarnings("unchecked")1429public void forEachRemaining(Consumer<? super K> action) {1430if (action == null)1431throw new NullPointerException();1432int i, hi, mc; Object key;1433IdentityHashMap<K,V> m; Object[] a;1434if ((m = map) != null && (a = m.table) != null &&1435(i = index) >= 0 && (index = hi = getFence()) <= a.length) {1436for (; i < hi; i += 2) {1437if ((key = a[i]) != null)1438action.accept((K)unmaskNull(key));1439}1440if (m.modCount == expectedModCount)1441return;1442}1443throw new ConcurrentModificationException();1444}14451446@SuppressWarnings("unchecked")1447public boolean tryAdvance(Consumer<? super K> action) {1448if (action == null)1449throw new NullPointerException();1450Object[] a = map.table;1451int hi = getFence();1452while (index < hi) {1453Object key = a[index];1454index += 2;1455if (key != null) {1456action.accept((K)unmaskNull(key));1457if (map.modCount != expectedModCount)1458throw new ConcurrentModificationException();1459return true;1460}1461}1462return false;1463}14641465public int characteristics() {1466return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;1467}1468}14691470static final class ValueSpliterator<K,V>1471extends IdentityHashMapSpliterator<K,V>1472implements Spliterator<V> {1473ValueSpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,1474int expectedModCount) {1475super(m, origin, fence, est, expectedModCount);1476}14771478public ValueSpliterator<K,V> trySplit() {1479int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;1480return (lo >= mid) ? null :1481new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,1482expectedModCount);1483}14841485public void forEachRemaining(Consumer<? super V> action) {1486if (action == null)1487throw new NullPointerException();1488int i, hi, mc;1489IdentityHashMap<K,V> m; Object[] a;1490if ((m = map) != null && (a = m.table) != null &&1491(i = index) >= 0 && (index = hi = getFence()) <= a.length) {1492for (; i < hi; i += 2) {1493if (a[i] != null) {1494@SuppressWarnings("unchecked") V v = (V)a[i+1];1495action.accept(v);1496}1497}1498if (m.modCount == expectedModCount)1499return;1500}1501throw new ConcurrentModificationException();1502}15031504public boolean tryAdvance(Consumer<? super V> action) {1505if (action == null)1506throw new NullPointerException();1507Object[] a = map.table;1508int hi = getFence();1509while (index < hi) {1510Object key = a[index];1511@SuppressWarnings("unchecked") V v = (V)a[index+1];1512index += 2;1513if (key != null) {1514action.accept(v);1515if (map.modCount != expectedModCount)1516throw new ConcurrentModificationException();1517return true;1518}1519}1520return false;1521}15221523public int characteristics() {1524return (fence < 0 || est == map.size ? SIZED : 0);1525}15261527}15281529static final class EntrySpliterator<K,V>1530extends IdentityHashMapSpliterator<K,V>1531implements Spliterator<Map.Entry<K,V>> {1532EntrySpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,1533int expectedModCount) {1534super(m, origin, fence, est, expectedModCount);1535}15361537public EntrySpliterator<K,V> trySplit() {1538int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;1539return (lo >= mid) ? null :1540new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,1541expectedModCount);1542}15431544public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {1545if (action == null)1546throw new NullPointerException();1547int i, hi, mc;1548IdentityHashMap<K,V> m; Object[] a;1549if ((m = map) != null && (a = m.table) != null &&1550(i = index) >= 0 && (index = hi = getFence()) <= a.length) {1551for (; i < hi; i += 2) {1552Object key = a[i];1553if (key != null) {1554@SuppressWarnings("unchecked") K k =1555(K)unmaskNull(key);1556@SuppressWarnings("unchecked") V v = (V)a[i+1];1557action.accept1558(new AbstractMap.SimpleImmutableEntry<>(k, v));15591560}1561}1562if (m.modCount == expectedModCount)1563return;1564}1565throw new ConcurrentModificationException();1566}15671568public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {1569if (action == null)1570throw new NullPointerException();1571Object[] a = map.table;1572int hi = getFence();1573while (index < hi) {1574Object key = a[index];1575@SuppressWarnings("unchecked") V v = (V)a[index+1];1576index += 2;1577if (key != null) {1578@SuppressWarnings("unchecked") K k =1579(K)unmaskNull(key);1580action.accept1581(new AbstractMap.SimpleImmutableEntry<>(k, v));1582if (map.modCount != expectedModCount)1583throw new ConcurrentModificationException();1584return true;1585}1586}1587return false;1588}15891590public int characteristics() {1591return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;1592}1593}15941595}159615971598