Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/java/util/IdentityHashMap.java
41152 views
1
/*
2
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package java.util;
27
28
import java.lang.reflect.Array;
29
import java.util.function.BiConsumer;
30
import java.util.function.BiFunction;
31
import java.util.function.Consumer;
32
import jdk.internal.access.SharedSecrets;
33
34
/**
35
* This class implements the {@code Map} interface with a hash table, using
36
* reference-equality in place of object-equality when comparing keys (and
37
* values). In other words, in an {@code IdentityHashMap}, two keys
38
* {@code k1} and {@code k2} are considered equal if and only if
39
* {@code (k1==k2)}. (In normal {@code Map} implementations (like
40
* {@code HashMap}) two keys {@code k1} and {@code k2} are considered equal
41
* if and only if {@code (k1==null ? k2==null : k1.equals(k2))}.)
42
*
43
* <p><b>This class is <i>not</i> a general-purpose {@code Map}
44
* implementation! While this class implements the {@code Map} interface, it
45
* intentionally violates {@code Map's} general contract, which mandates the
46
* use of the {@code equals} method when comparing objects. This class is
47
* designed for use only in the rare cases wherein reference-equality
48
* semantics are required.</b>
49
*
50
* <p>A typical use of this class is <i>topology-preserving object graph
51
* transformations</i>, such as serialization or deep-copying. To perform such
52
* a transformation, a program must maintain a "node table" that keeps track
53
* of all the object references that have already been processed. The node
54
* table must not equate distinct objects even if they happen to be equal.
55
* Another typical use of this class is to maintain <i>proxy objects</i>. For
56
* example, a debugging facility might wish to maintain a proxy object for
57
* each object in the program being debugged.
58
*
59
* <p>This class provides all of the optional map operations, and permits
60
* {@code null} values and the {@code null} key. This class makes no
61
* guarantees as to the order of the map; in particular, it does not guarantee
62
* that the order will remain constant over time.
63
*
64
* <p>This class provides constant-time performance for the basic
65
* operations ({@code get} and {@code put}), assuming the system
66
* identity hash function ({@link System#identityHashCode(Object)})
67
* disperses elements properly among the buckets.
68
*
69
* <p>This class has one tuning parameter (which affects performance but not
70
* semantics): <i>expected maximum size</i>. This parameter is the maximum
71
* number of key-value mappings that the map is expected to hold. Internally,
72
* this parameter is used to determine the number of buckets initially
73
* comprising the hash table. The precise relationship between the expected
74
* maximum size and the number of buckets is unspecified.
75
*
76
* <p>If the size of the map (the number of key-value mappings) sufficiently
77
* exceeds the expected maximum size, the number of buckets is increased.
78
* Increasing the number of buckets ("rehashing") may be fairly expensive, so
79
* it pays to create identity hash maps with a sufficiently large expected
80
* maximum size. On the other hand, iteration over collection views requires
81
* time proportional to the number of buckets in the hash table, so it
82
* pays not to set the expected maximum size too high if you are especially
83
* concerned with iteration performance or memory usage.
84
*
85
* <p><strong>Note that this implementation is not synchronized.</strong>
86
* If multiple threads access an identity hash map concurrently, and at
87
* least one of the threads modifies the map structurally, it <i>must</i>
88
* be synchronized externally. (A structural modification is any operation
89
* that adds or deletes one or more mappings; merely changing the value
90
* associated with a key that an instance already contains is not a
91
* structural modification.) This is typically accomplished by
92
* synchronizing on some object that naturally encapsulates the map.
93
*
94
* If no such object exists, the map should be "wrapped" using the
95
* {@link Collections#synchronizedMap Collections.synchronizedMap}
96
* method. This is best done at creation time, to prevent accidental
97
* unsynchronized access to the map:<pre>
98
* Map m = Collections.synchronizedMap(new IdentityHashMap(...));</pre>
99
*
100
* <p>The iterators returned by the {@code iterator} method of the
101
* collections returned by all of this class's "collection view
102
* methods" are <i>fail-fast</i>: if the map is structurally modified
103
* at any time after the iterator is created, in any way except
104
* through the iterator's own {@code remove} method, the iterator
105
* will throw a {@link ConcurrentModificationException}. Thus, in the
106
* face of concurrent modification, the iterator fails quickly and
107
* cleanly, rather than risking arbitrary, non-deterministic behavior
108
* at an undetermined time in the future.
109
*
110
* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
111
* as it is, generally speaking, impossible to make any hard guarantees in the
112
* presence of unsynchronized concurrent modification. Fail-fast iterators
113
* throw {@code ConcurrentModificationException} on a best-effort basis.
114
* Therefore, it would be wrong to write a program that depended on this
115
* exception for its correctness: <i>fail-fast iterators should be used only
116
* to detect bugs.</i>
117
*
118
* <p>This class is a member of the
119
* <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
120
* Java Collections Framework</a>.
121
*
122
* @implNote
123
* <p>This is a simple <i>linear-probe</i> hash table,
124
* as described for example in texts by Sedgewick and Knuth. The array
125
* contains alternating keys and values, with keys at even indexes and values
126
* at odd indexes. (This arrangement has better locality for large
127
* tables than does using separate arrays.) For many Java implementations
128
* and operation mixes, this class will yield better performance than
129
* {@link HashMap}, which uses <i>chaining</i> rather than linear-probing.
130
*
131
* @see System#identityHashCode(Object)
132
* @see Object#hashCode()
133
* @see Collection
134
* @see Map
135
* @see HashMap
136
* @see TreeMap
137
* @author Doug Lea and Josh Bloch
138
* @since 1.4
139
*/
140
141
public class IdentityHashMap<K,V>
142
extends AbstractMap<K,V>
143
implements Map<K,V>, java.io.Serializable, Cloneable
144
{
145
/**
146
* The initial capacity used by the no-args constructor.
147
* MUST be a power of two. The value 32 corresponds to the
148
* (specified) expected maximum size of 21, given a load factor
149
* of 2/3.
150
*/
151
private static final int DEFAULT_CAPACITY = 32;
152
153
/**
154
* The minimum capacity, used if a lower value is implicitly specified
155
* by either of the constructors with arguments. The value 4 corresponds
156
* to an expected maximum size of 2, given a load factor of 2/3.
157
* MUST be a power of two.
158
*/
159
private static final int MINIMUM_CAPACITY = 4;
160
161
/**
162
* The maximum capacity, used if a higher value is implicitly specified
163
* by either of the constructors with arguments.
164
* MUST be a power of two <= 1<<29.
165
*
166
* In fact, the map can hold no more than MAXIMUM_CAPACITY-1 items
167
* because it has to have at least one slot with the key == null
168
* in order to avoid infinite loops in get(), put(), remove()
169
*/
170
private static final int MAXIMUM_CAPACITY = 1 << 29;
171
172
/**
173
* The table, resized as necessary. Length MUST always be a power of two.
174
*/
175
transient Object[] table; // non-private to simplify nested class access
176
177
/**
178
* The number of key-value mappings contained in this identity hash map.
179
*
180
* @serial
181
*/
182
int size;
183
184
/**
185
* The number of modifications, to support fast-fail iterators
186
*/
187
transient int modCount;
188
189
/**
190
* Value representing null keys inside tables.
191
*/
192
static final Object NULL_KEY = new Object();
193
194
/**
195
* Use NULL_KEY for key if it is null.
196
*/
197
private static Object maskNull(Object key) {
198
return (key == null ? NULL_KEY : key);
199
}
200
201
/**
202
* Returns internal representation of null key back to caller as null.
203
*/
204
static final Object unmaskNull(Object key) {
205
return (key == NULL_KEY ? null : key);
206
}
207
208
/**
209
* Constructs a new, empty identity hash map with a default expected
210
* maximum size (21).
211
*/
212
public IdentityHashMap() {
213
init(DEFAULT_CAPACITY);
214
}
215
216
/**
217
* Constructs a new, empty map with the specified expected maximum size.
218
* Putting more than the expected number of key-value mappings into
219
* the map may cause the internal data structure to grow, which may be
220
* somewhat time-consuming.
221
*
222
* @param expectedMaxSize the expected maximum size of the map
223
* @throws IllegalArgumentException if {@code expectedMaxSize} is negative
224
*/
225
public IdentityHashMap(int expectedMaxSize) {
226
if (expectedMaxSize < 0)
227
throw new IllegalArgumentException("expectedMaxSize is negative: "
228
+ expectedMaxSize);
229
init(capacity(expectedMaxSize));
230
}
231
232
/**
233
* Returns the appropriate capacity for the given expected maximum size.
234
* Returns the smallest power of two between MINIMUM_CAPACITY and
235
* MAXIMUM_CAPACITY, inclusive, that is greater than (3 *
236
* expectedMaxSize)/2, if such a number exists. Otherwise returns
237
* MAXIMUM_CAPACITY.
238
*/
239
private static int capacity(int expectedMaxSize) {
240
// assert expectedMaxSize >= 0;
241
return
242
(expectedMaxSize > MAXIMUM_CAPACITY / 3) ? MAXIMUM_CAPACITY :
243
(expectedMaxSize <= 2 * MINIMUM_CAPACITY / 3) ? MINIMUM_CAPACITY :
244
Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1));
245
}
246
247
/**
248
* Initializes object to be an empty map with the specified initial
249
* capacity, which is assumed to be a power of two between
250
* MINIMUM_CAPACITY and MAXIMUM_CAPACITY inclusive.
251
*/
252
private void init(int initCapacity) {
253
// assert (initCapacity & -initCapacity) == initCapacity; // power of 2
254
// assert initCapacity >= MINIMUM_CAPACITY;
255
// assert initCapacity <= MAXIMUM_CAPACITY;
256
257
table = new Object[2 * initCapacity];
258
}
259
260
/**
261
* Constructs a new identity hash map containing the keys-value mappings
262
* in the specified map.
263
*
264
* @param m the map whose mappings are to be placed into this map
265
* @throws NullPointerException if the specified map is null
266
*/
267
public IdentityHashMap(Map<? extends K, ? extends V> m) {
268
// Allow for a bit of growth
269
this((int) ((1 + m.size()) * 1.1));
270
putAll(m);
271
}
272
273
/**
274
* Returns the number of key-value mappings in this identity hash map.
275
*
276
* @return the number of key-value mappings in this map
277
*/
278
public int size() {
279
return size;
280
}
281
282
/**
283
* Returns {@code true} if this identity hash map contains no key-value
284
* mappings.
285
*
286
* @return {@code true} if this identity hash map contains no key-value
287
* mappings
288
*/
289
public boolean isEmpty() {
290
return size == 0;
291
}
292
293
/**
294
* Returns index for Object x.
295
*/
296
private static int hash(Object x, int length) {
297
int h = System.identityHashCode(x);
298
// Multiply by -254 to use the hash LSB and to ensure index is even
299
return ((h << 1) - (h << 8)) & (length - 1);
300
}
301
302
/**
303
* Circularly traverses table of size len.
304
*/
305
private static int nextKeyIndex(int i, int len) {
306
return (i + 2 < len ? i + 2 : 0);
307
}
308
309
/**
310
* Returns the value to which the specified key is mapped,
311
* or {@code null} if this map contains no mapping for the key.
312
*
313
* <p>More formally, if this map contains a mapping from a key
314
* {@code k} to a value {@code v} such that {@code (key == k)},
315
* then this method returns {@code v}; otherwise it returns
316
* {@code null}. (There can be at most one such mapping.)
317
*
318
* <p>A return value of {@code null} does not <i>necessarily</i>
319
* indicate that the map contains no mapping for the key; it's also
320
* possible that the map explicitly maps the key to {@code null}.
321
* The {@link #containsKey containsKey} operation may be used to
322
* distinguish these two cases.
323
*
324
* @see #put(Object, Object)
325
*/
326
@SuppressWarnings("unchecked")
327
public V get(Object key) {
328
Object k = maskNull(key);
329
Object[] tab = table;
330
int len = tab.length;
331
int i = hash(k, len);
332
while (true) {
333
Object item = tab[i];
334
if (item == k)
335
return (V) tab[i + 1];
336
if (item == null)
337
return null;
338
i = nextKeyIndex(i, len);
339
}
340
}
341
342
/**
343
* Tests whether the specified object reference is a key in this identity
344
* hash map.
345
*
346
* @param key possible key
347
* @return {@code true} if the specified object reference is a key
348
* in this map
349
* @see #containsValue(Object)
350
*/
351
public boolean containsKey(Object key) {
352
Object k = maskNull(key);
353
Object[] tab = table;
354
int len = tab.length;
355
int i = hash(k, len);
356
while (true) {
357
Object item = tab[i];
358
if (item == k)
359
return true;
360
if (item == null)
361
return false;
362
i = nextKeyIndex(i, len);
363
}
364
}
365
366
/**
367
* Tests whether the specified object reference is a value in this identity
368
* hash map.
369
*
370
* @param value value whose presence in this map is to be tested
371
* @return {@code true} if this map maps one or more keys to the
372
* specified object reference
373
* @see #containsKey(Object)
374
*/
375
public boolean containsValue(Object value) {
376
Object[] tab = table;
377
for (int i = 1; i < tab.length; i += 2)
378
if (tab[i] == value && tab[i - 1] != null)
379
return true;
380
381
return false;
382
}
383
384
/**
385
* Tests if the specified key-value mapping is in the map.
386
*
387
* @param key possible key
388
* @param value possible value
389
* @return {@code true} if and only if the specified key-value
390
* mapping is in the map
391
*/
392
private boolean containsMapping(Object key, Object value) {
393
Object k = maskNull(key);
394
Object[] tab = table;
395
int len = tab.length;
396
int i = hash(k, len);
397
while (true) {
398
Object item = tab[i];
399
if (item == k)
400
return tab[i + 1] == value;
401
if (item == null)
402
return false;
403
i = nextKeyIndex(i, len);
404
}
405
}
406
407
/**
408
* Associates the specified value with the specified key in this identity
409
* hash map. If the map previously contained a mapping for the key, the
410
* old value is replaced.
411
*
412
* @param key the key with which the specified value is to be associated
413
* @param value the value to be associated with the specified key
414
* @return the previous value associated with {@code key}, or
415
* {@code null} if there was no mapping for {@code key}.
416
* (A {@code null} return can also indicate that the map
417
* previously associated {@code null} with {@code key}.)
418
* @see Object#equals(Object)
419
* @see #get(Object)
420
* @see #containsKey(Object)
421
*/
422
public V put(K key, V value) {
423
final Object k = maskNull(key);
424
425
retryAfterResize: for (;;) {
426
final Object[] tab = table;
427
final int len = tab.length;
428
int i = hash(k, len);
429
430
for (Object item; (item = tab[i]) != null;
431
i = nextKeyIndex(i, len)) {
432
if (item == k) {
433
@SuppressWarnings("unchecked")
434
V oldValue = (V) tab[i + 1];
435
tab[i + 1] = value;
436
return oldValue;
437
}
438
}
439
440
final int s = size + 1;
441
// Use optimized form of 3 * s.
442
// Next capacity is len, 2 * current capacity.
443
if (s + (s << 1) > len && resize(len))
444
continue retryAfterResize;
445
446
modCount++;
447
tab[i] = k;
448
tab[i + 1] = value;
449
size = s;
450
return null;
451
}
452
}
453
454
/**
455
* Resizes the table if necessary to hold given capacity.
456
*
457
* @param newCapacity the new capacity, must be a power of two.
458
* @return whether a resize did in fact take place
459
*/
460
private boolean resize(int newCapacity) {
461
// assert (newCapacity & -newCapacity) == newCapacity; // power of 2
462
int newLength = newCapacity * 2;
463
464
Object[] oldTable = table;
465
int oldLength = oldTable.length;
466
if (oldLength == 2 * MAXIMUM_CAPACITY) { // can't expand any further
467
if (size == MAXIMUM_CAPACITY - 1)
468
throw new IllegalStateException("Capacity exhausted.");
469
return false;
470
}
471
if (oldLength >= newLength)
472
return false;
473
474
Object[] newTable = new Object[newLength];
475
476
for (int j = 0; j < oldLength; j += 2) {
477
Object key = oldTable[j];
478
if (key != null) {
479
Object value = oldTable[j+1];
480
oldTable[j] = null;
481
oldTable[j+1] = null;
482
int i = hash(key, newLength);
483
while (newTable[i] != null)
484
i = nextKeyIndex(i, newLength);
485
newTable[i] = key;
486
newTable[i + 1] = value;
487
}
488
}
489
table = newTable;
490
return true;
491
}
492
493
/**
494
* Copies all of the mappings from the specified map to this map.
495
* These mappings will replace any mappings that this map had for
496
* any of the keys currently in the specified map.
497
*
498
* @param m mappings to be stored in this map
499
* @throws NullPointerException if the specified map is null
500
*/
501
public void putAll(Map<? extends K, ? extends V> m) {
502
int n = m.size();
503
if (n == 0)
504
return;
505
if (n > size)
506
resize(capacity(n)); // conservatively pre-expand
507
508
for (Entry<? extends K, ? extends V> e : m.entrySet())
509
put(e.getKey(), e.getValue());
510
}
511
512
/**
513
* Removes the mapping for this key from this map if present.
514
*
515
* @param key key whose mapping is to be removed from the map
516
* @return the previous value associated with {@code key}, or
517
* {@code null} if there was no mapping for {@code key}.
518
* (A {@code null} return can also indicate that the map
519
* previously associated {@code null} with {@code key}.)
520
*/
521
public V remove(Object key) {
522
Object k = maskNull(key);
523
Object[] tab = table;
524
int len = tab.length;
525
int i = hash(k, len);
526
527
while (true) {
528
Object item = tab[i];
529
if (item == k) {
530
modCount++;
531
size--;
532
@SuppressWarnings("unchecked")
533
V oldValue = (V) tab[i + 1];
534
tab[i + 1] = null;
535
tab[i] = null;
536
closeDeletion(i);
537
return oldValue;
538
}
539
if (item == null)
540
return null;
541
i = nextKeyIndex(i, len);
542
}
543
}
544
545
/**
546
* Removes the specified key-value mapping from the map if it is present.
547
*
548
* @param key possible key
549
* @param value possible value
550
* @return {@code true} if and only if the specified key-value
551
* mapping was in the map
552
*/
553
private boolean removeMapping(Object key, Object value) {
554
Object k = maskNull(key);
555
Object[] tab = table;
556
int len = tab.length;
557
int i = hash(k, len);
558
559
while (true) {
560
Object item = tab[i];
561
if (item == k) {
562
if (tab[i + 1] != value)
563
return false;
564
modCount++;
565
size--;
566
tab[i] = null;
567
tab[i + 1] = null;
568
closeDeletion(i);
569
return true;
570
}
571
if (item == null)
572
return false;
573
i = nextKeyIndex(i, len);
574
}
575
}
576
577
/**
578
* Rehash all possibly-colliding entries following a
579
* deletion. This preserves the linear-probe
580
* collision properties required by get, put, etc.
581
*
582
* @param d the index of a newly empty deleted slot
583
*/
584
private void closeDeletion(int d) {
585
// Adapted from Knuth Section 6.4 Algorithm R
586
Object[] tab = table;
587
int len = tab.length;
588
589
// Look for items to swap into newly vacated slot
590
// starting at index immediately following deletion,
591
// and continuing until a null slot is seen, indicating
592
// the end of a run of possibly-colliding keys.
593
Object item;
594
for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
595
i = nextKeyIndex(i, len) ) {
596
// The following test triggers if the item at slot i (which
597
// hashes to be at slot r) should take the spot vacated by d.
598
// If so, we swap it in, and then continue with d now at the
599
// newly vacated i. This process will terminate when we hit
600
// the null slot at the end of this run.
601
// The test is messy because we are using a circular table.
602
int r = hash(item, len);
603
if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {
604
tab[d] = item;
605
tab[d + 1] = tab[i + 1];
606
tab[i] = null;
607
tab[i + 1] = null;
608
d = i;
609
}
610
}
611
}
612
613
/**
614
* Removes all of the mappings from this map.
615
* The map will be empty after this call returns.
616
*/
617
public void clear() {
618
modCount++;
619
Object[] tab = table;
620
for (int i = 0; i < tab.length; i++)
621
tab[i] = null;
622
size = 0;
623
}
624
625
/**
626
* Compares the specified object with this map for equality. Returns
627
* {@code true} if the given object is also a map and the two maps
628
* represent identical object-reference mappings. More formally, this
629
* map is equal to another map {@code m} if and only if
630
* {@code this.entrySet().equals(m.entrySet())}.
631
*
632
* <p><b>Owing to the reference-equality-based semantics of this map it is
633
* possible that the symmetry and transitivity requirements of the
634
* {@code Object.equals} contract may be violated if this map is compared
635
* to a normal map. However, the {@code Object.equals} contract is
636
* guaranteed to hold among {@code IdentityHashMap} instances.</b>
637
*
638
* @param o object to be compared for equality with this map
639
* @return {@code true} if the specified object is equal to this map
640
* @see Object#equals(Object)
641
*/
642
public boolean equals(Object o) {
643
if (o == this) {
644
return true;
645
} else if (o instanceof IdentityHashMap<?, ?> m) {
646
if (m.size() != size)
647
return false;
648
649
Object[] tab = m.table;
650
for (int i = 0; i < tab.length; i+=2) {
651
Object k = tab[i];
652
if (k != null && !containsMapping(k, tab[i + 1]))
653
return false;
654
}
655
return true;
656
} else if (o instanceof Map<?, ?> m) {
657
return entrySet().equals(m.entrySet());
658
} else {
659
return false; // o is not a Map
660
}
661
}
662
663
/**
664
* Returns the hash code value for this map. The hash code of a map is
665
* defined to be the sum of the hash codes of each entry in the map's
666
* {@code entrySet()} view. This ensures that {@code m1.equals(m2)}
667
* implies that {@code m1.hashCode()==m2.hashCode()} for any two
668
* {@code IdentityHashMap} instances {@code m1} and {@code m2}, as
669
* required by the general contract of {@link Object#hashCode}.
670
*
671
* <p><b>Owing to the reference-equality-based semantics of the
672
* {@code Map.Entry} instances in the set returned by this map's
673
* {@code entrySet} method, it is possible that the contractual
674
* requirement of {@code Object.hashCode} mentioned in the previous
675
* paragraph will be violated if one of the two objects being compared is
676
* an {@code IdentityHashMap} instance and the other is a normal map.</b>
677
*
678
* @return the hash code value for this map
679
* @see Object#equals(Object)
680
* @see #equals(Object)
681
*/
682
public int hashCode() {
683
int result = 0;
684
Object[] tab = table;
685
for (int i = 0; i < tab.length; i +=2) {
686
Object key = tab[i];
687
if (key != null) {
688
Object k = unmaskNull(key);
689
result += System.identityHashCode(k) ^
690
System.identityHashCode(tab[i + 1]);
691
}
692
}
693
return result;
694
}
695
696
/**
697
* Returns a shallow copy of this identity hash map: the keys and values
698
* themselves are not cloned.
699
*
700
* @return a shallow copy of this map
701
*/
702
public Object clone() {
703
try {
704
IdentityHashMap<?,?> m = (IdentityHashMap<?,?>) super.clone();
705
m.entrySet = null;
706
m.table = table.clone();
707
return m;
708
} catch (CloneNotSupportedException e) {
709
throw new InternalError(e);
710
}
711
}
712
713
private abstract class IdentityHashMapIterator<T> implements Iterator<T> {
714
int index = (size != 0 ? 0 : table.length); // current slot.
715
int expectedModCount = modCount; // to support fast-fail
716
int lastReturnedIndex = -1; // to allow remove()
717
boolean indexValid; // To avoid unnecessary next computation
718
Object[] traversalTable = table; // reference to main table or copy
719
720
public boolean hasNext() {
721
Object[] tab = traversalTable;
722
for (int i = index; i < tab.length; i+=2) {
723
Object key = tab[i];
724
if (key != null) {
725
index = i;
726
return indexValid = true;
727
}
728
}
729
index = tab.length;
730
return false;
731
}
732
733
protected int nextIndex() {
734
if (modCount != expectedModCount)
735
throw new ConcurrentModificationException();
736
if (!indexValid && !hasNext())
737
throw new NoSuchElementException();
738
739
indexValid = false;
740
lastReturnedIndex = index;
741
index += 2;
742
return lastReturnedIndex;
743
}
744
745
public void remove() {
746
if (lastReturnedIndex == -1)
747
throw new IllegalStateException();
748
if (modCount != expectedModCount)
749
throw new ConcurrentModificationException();
750
751
expectedModCount = ++modCount;
752
int deletedSlot = lastReturnedIndex;
753
lastReturnedIndex = -1;
754
// back up index to revisit new contents after deletion
755
index = deletedSlot;
756
indexValid = false;
757
758
// Removal code proceeds as in closeDeletion except that
759
// it must catch the rare case where an element already
760
// seen is swapped into a vacant slot that will be later
761
// traversed by this iterator. We cannot allow future
762
// next() calls to return it again. The likelihood of
763
// this occurring under 2/3 load factor is very slim, but
764
// when it does happen, we must make a copy of the rest of
765
// the table to use for the rest of the traversal. Since
766
// this can only happen when we are near the end of the table,
767
// even in these rare cases, this is not very expensive in
768
// time or space.
769
770
Object[] tab = traversalTable;
771
int len = tab.length;
772
773
int d = deletedSlot;
774
Object key = tab[d];
775
tab[d] = null; // vacate the slot
776
tab[d + 1] = null;
777
778
// If traversing a copy, remove in real table.
779
// We can skip gap-closure on copy.
780
if (tab != IdentityHashMap.this.table) {
781
IdentityHashMap.this.remove(key);
782
expectedModCount = modCount;
783
return;
784
}
785
786
size--;
787
788
Object item;
789
for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
790
i = nextKeyIndex(i, len)) {
791
int r = hash(item, len);
792
// See closeDeletion for explanation of this conditional
793
if ((i < r && (r <= d || d <= i)) ||
794
(r <= d && d <= i)) {
795
796
// If we are about to swap an already-seen element
797
// into a slot that may later be returned by next(),
798
// then clone the rest of table for use in future
799
// next() calls. It is OK that our copy will have
800
// a gap in the "wrong" place, since it will never
801
// be used for searching anyway.
802
803
if (i < deletedSlot && d >= deletedSlot &&
804
traversalTable == IdentityHashMap.this.table) {
805
int remaining = len - deletedSlot;
806
Object[] newTable = new Object[remaining];
807
System.arraycopy(tab, deletedSlot,
808
newTable, 0, remaining);
809
traversalTable = newTable;
810
index = 0;
811
}
812
813
tab[d] = item;
814
tab[d + 1] = tab[i + 1];
815
tab[i] = null;
816
tab[i + 1] = null;
817
d = i;
818
}
819
}
820
}
821
}
822
823
private class KeyIterator extends IdentityHashMapIterator<K> {
824
@SuppressWarnings("unchecked")
825
public K next() {
826
return (K) unmaskNull(traversalTable[nextIndex()]);
827
}
828
}
829
830
private class ValueIterator extends IdentityHashMapIterator<V> {
831
@SuppressWarnings("unchecked")
832
public V next() {
833
return (V) traversalTable[nextIndex() + 1];
834
}
835
}
836
837
private class EntryIterator
838
extends IdentityHashMapIterator<Map.Entry<K,V>>
839
{
840
private Entry lastReturnedEntry;
841
842
public Map.Entry<K,V> next() {
843
lastReturnedEntry = new Entry(nextIndex());
844
return lastReturnedEntry;
845
}
846
847
public void remove() {
848
lastReturnedIndex =
849
((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
850
super.remove();
851
lastReturnedEntry.index = lastReturnedIndex;
852
lastReturnedEntry = null;
853
}
854
855
private class Entry implements Map.Entry<K,V> {
856
private int index;
857
858
private Entry(int index) {
859
this.index = index;
860
}
861
862
@SuppressWarnings("unchecked")
863
public K getKey() {
864
checkIndexForEntryUse();
865
return (K) unmaskNull(traversalTable[index]);
866
}
867
868
@SuppressWarnings("unchecked")
869
public V getValue() {
870
checkIndexForEntryUse();
871
return (V) traversalTable[index+1];
872
}
873
874
@SuppressWarnings("unchecked")
875
public V setValue(V value) {
876
checkIndexForEntryUse();
877
V oldValue = (V) traversalTable[index+1];
878
traversalTable[index+1] = value;
879
// if shadowing, force into main table
880
if (traversalTable != IdentityHashMap.this.table)
881
put((K) traversalTable[index], value);
882
return oldValue;
883
}
884
885
public boolean equals(Object o) {
886
if (index < 0)
887
return super.equals(o);
888
889
return o instanceof Map.Entry<?, ?> e
890
&& e.getKey() == unmaskNull(traversalTable[index])
891
&& e.getValue() == traversalTable[index+1];
892
}
893
894
public int hashCode() {
895
if (lastReturnedIndex < 0)
896
return super.hashCode();
897
898
return (System.identityHashCode(unmaskNull(traversalTable[index])) ^
899
System.identityHashCode(traversalTable[index+1]));
900
}
901
902
public String toString() {
903
if (index < 0)
904
return super.toString();
905
906
return (unmaskNull(traversalTable[index]) + "="
907
+ traversalTable[index+1]);
908
}
909
910
private void checkIndexForEntryUse() {
911
if (index < 0)
912
throw new IllegalStateException("Entry was removed");
913
}
914
}
915
}
916
917
// Views
918
919
/**
920
* This field is initialized to contain an instance of the entry set
921
* view the first time this view is requested. The view is stateless,
922
* so there's no reason to create more than one.
923
*/
924
private transient Set<Map.Entry<K,V>> entrySet;
925
926
/**
927
* Returns an identity-based set view of the keys contained in this map.
928
* The set is backed by the map, so changes to the map are reflected in
929
* the set, and vice-versa. If the map is modified while an iteration
930
* over the set is in progress, the results of the iteration are
931
* undefined. The set supports element removal, which removes the
932
* corresponding mapping from the map, via the {@code Iterator.remove},
933
* {@code Set.remove}, {@code removeAll}, {@code retainAll}, and
934
* {@code clear} methods. It does not support the {@code add} or
935
* {@code addAll} methods.
936
*
937
* <p><b>While the object returned by this method implements the
938
* {@code Set} interface, it does <i>not</i> obey {@code Set's} general
939
* contract. Like its backing map, the set returned by this method
940
* defines element equality as reference-equality rather than
941
* object-equality. This affects the behavior of its {@code contains},
942
* {@code remove}, {@code containsAll}, {@code equals}, and
943
* {@code hashCode} methods.</b>
944
*
945
* <p><b>The {@code equals} method of the returned set returns {@code true}
946
* only if the specified object is a set containing exactly the same
947
* object references as the returned set. The symmetry and transitivity
948
* requirements of the {@code Object.equals} contract may be violated if
949
* the set returned by this method is compared to a normal set. However,
950
* the {@code Object.equals} contract is guaranteed to hold among sets
951
* returned by this method.</b>
952
*
953
* <p>The {@code hashCode} method of the returned set returns the sum of
954
* the <i>identity hashcodes</i> of the elements in the set, rather than
955
* the sum of their hashcodes. This is mandated by the change in the
956
* semantics of the {@code equals} method, in order to enforce the
957
* general contract of the {@code Object.hashCode} method among sets
958
* returned by this method.
959
*
960
* @return an identity-based set view of the keys contained in this map
961
* @see Object#equals(Object)
962
* @see System#identityHashCode(Object)
963
*/
964
public Set<K> keySet() {
965
Set<K> ks = keySet;
966
if (ks == null) {
967
ks = new KeySet();
968
keySet = ks;
969
}
970
return ks;
971
}
972
973
private class KeySet extends AbstractSet<K> {
974
public Iterator<K> iterator() {
975
return new KeyIterator();
976
}
977
public int size() {
978
return size;
979
}
980
public boolean contains(Object o) {
981
return containsKey(o);
982
}
983
public boolean remove(Object o) {
984
int oldSize = size;
985
IdentityHashMap.this.remove(o);
986
return size != oldSize;
987
}
988
/*
989
* Must revert from AbstractSet's impl to AbstractCollection's, as
990
* the former contains an optimization that results in incorrect
991
* behavior when c is a smaller "normal" (non-identity-based) Set.
992
*/
993
public boolean removeAll(Collection<?> c) {
994
Objects.requireNonNull(c);
995
boolean modified = false;
996
for (Iterator<K> i = iterator(); i.hasNext(); ) {
997
if (c.contains(i.next())) {
998
i.remove();
999
modified = true;
1000
}
1001
}
1002
return modified;
1003
}
1004
public void clear() {
1005
IdentityHashMap.this.clear();
1006
}
1007
public int hashCode() {
1008
int result = 0;
1009
for (K key : this)
1010
result += System.identityHashCode(key);
1011
return result;
1012
}
1013
public Object[] toArray() {
1014
return toArray(new Object[0]);
1015
}
1016
@SuppressWarnings("unchecked")
1017
public <T> T[] toArray(T[] a) {
1018
int expectedModCount = modCount;
1019
int size = size();
1020
if (a.length < size)
1021
a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
1022
Object[] tab = table;
1023
int ti = 0;
1024
for (int si = 0; si < tab.length; si += 2) {
1025
Object key;
1026
if ((key = tab[si]) != null) { // key present ?
1027
// more elements than expected -> concurrent modification from other thread
1028
if (ti >= size) {
1029
throw new ConcurrentModificationException();
1030
}
1031
a[ti++] = (T) unmaskNull(key); // unmask key
1032
}
1033
}
1034
// fewer elements than expected or concurrent modification from other thread detected
1035
if (ti < size || expectedModCount != modCount) {
1036
throw new ConcurrentModificationException();
1037
}
1038
// final null marker as per spec
1039
if (ti < a.length) {
1040
a[ti] = null;
1041
}
1042
return a;
1043
}
1044
1045
public Spliterator<K> spliterator() {
1046
return new KeySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
1047
}
1048
}
1049
1050
/**
1051
* Returns a {@link Collection} view of the values contained in this map.
1052
* The collection is backed by the map, so changes to the map are
1053
* reflected in the collection, and vice-versa. If the map is
1054
* modified while an iteration over the collection is in progress,
1055
* the results of the iteration are undefined. The collection
1056
* supports element removal, which removes the corresponding
1057
* mapping from the map, via the {@code Iterator.remove},
1058
* {@code Collection.remove}, {@code removeAll},
1059
* {@code retainAll} and {@code clear} methods. It does not
1060
* support the {@code add} or {@code addAll} methods.
1061
*
1062
* <p><b>While the object returned by this method implements the
1063
* {@code Collection} interface, it does <i>not</i> obey
1064
* {@code Collection's} general contract. Like its backing map,
1065
* the collection returned by this method defines element equality as
1066
* reference-equality rather than object-equality. This affects the
1067
* behavior of its {@code contains}, {@code remove} and
1068
* {@code containsAll} methods.</b>
1069
*/
1070
public Collection<V> values() {
1071
Collection<V> vs = values;
1072
if (vs == null) {
1073
vs = new Values();
1074
values = vs;
1075
}
1076
return vs;
1077
}
1078
1079
private class Values extends AbstractCollection<V> {
1080
public Iterator<V> iterator() {
1081
return new ValueIterator();
1082
}
1083
public int size() {
1084
return size;
1085
}
1086
public boolean contains(Object o) {
1087
return containsValue(o);
1088
}
1089
public boolean remove(Object o) {
1090
for (Iterator<V> i = iterator(); i.hasNext(); ) {
1091
if (i.next() == o) {
1092
i.remove();
1093
return true;
1094
}
1095
}
1096
return false;
1097
}
1098
public void clear() {
1099
IdentityHashMap.this.clear();
1100
}
1101
public Object[] toArray() {
1102
return toArray(new Object[0]);
1103
}
1104
@SuppressWarnings("unchecked")
1105
public <T> T[] toArray(T[] a) {
1106
int expectedModCount = modCount;
1107
int size = size();
1108
if (a.length < size)
1109
a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
1110
Object[] tab = table;
1111
int ti = 0;
1112
for (int si = 0; si < tab.length; si += 2) {
1113
if (tab[si] != null) { // key present ?
1114
// more elements than expected -> concurrent modification from other thread
1115
if (ti >= size) {
1116
throw new ConcurrentModificationException();
1117
}
1118
a[ti++] = (T) tab[si+1]; // copy value
1119
}
1120
}
1121
// fewer elements than expected or concurrent modification from other thread detected
1122
if (ti < size || expectedModCount != modCount) {
1123
throw new ConcurrentModificationException();
1124
}
1125
// final null marker as per spec
1126
if (ti < a.length) {
1127
a[ti] = null;
1128
}
1129
return a;
1130
}
1131
1132
public Spliterator<V> spliterator() {
1133
return new ValueSpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
1134
}
1135
}
1136
1137
/**
1138
* Returns a {@link Set} view of the mappings contained in this map.
1139
* Each element in the returned set is a reference-equality-based
1140
* {@code Map.Entry}. The set is backed by the map, so changes
1141
* to the map are reflected in the set, and vice-versa. If the
1142
* map is modified while an iteration over the set is in progress,
1143
* the results of the iteration are undefined. The set supports
1144
* element removal, which removes the corresponding mapping from
1145
* the map, via the {@code Iterator.remove}, {@code Set.remove},
1146
* {@code removeAll}, {@code retainAll} and {@code clear}
1147
* methods. It does not support the {@code add} or
1148
* {@code addAll} methods.
1149
*
1150
* <p>Like the backing map, the {@code Map.Entry} objects in the set
1151
* returned by this method define key and value equality as
1152
* reference-equality rather than object-equality. This affects the
1153
* behavior of the {@code equals} and {@code hashCode} methods of these
1154
* {@code Map.Entry} objects. A reference-equality based {@code Map.Entry
1155
* e} is equal to an object {@code o} if and only if {@code o} is a
1156
* {@code Map.Entry} and {@code e.getKey()==o.getKey() &&
1157
* e.getValue()==o.getValue()}. To accommodate these equals
1158
* semantics, the {@code hashCode} method returns
1159
* {@code System.identityHashCode(e.getKey()) ^
1160
* System.identityHashCode(e.getValue())}.
1161
*
1162
* <p><b>Owing to the reference-equality-based semantics of the
1163
* {@code Map.Entry} instances in the set returned by this method,
1164
* it is possible that the symmetry and transitivity requirements of
1165
* the {@link Object#equals(Object)} contract may be violated if any of
1166
* the entries in the set is compared to a normal map entry, or if
1167
* the set returned by this method is compared to a set of normal map
1168
* entries (such as would be returned by a call to this method on a normal
1169
* map). However, the {@code Object.equals} contract is guaranteed to
1170
* hold among identity-based map entries, and among sets of such entries.
1171
* </b>
1172
*
1173
* @return a set view of the identity-mappings contained in this map
1174
*/
1175
public Set<Map.Entry<K,V>> entrySet() {
1176
Set<Map.Entry<K,V>> es = entrySet;
1177
if (es != null)
1178
return es;
1179
else
1180
return entrySet = new EntrySet();
1181
}
1182
1183
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
1184
public Iterator<Map.Entry<K,V>> iterator() {
1185
return new EntryIterator();
1186
}
1187
public boolean contains(Object o) {
1188
return o instanceof Entry<?, ?> entry
1189
&& containsMapping(entry.getKey(), entry.getValue());
1190
}
1191
public boolean remove(Object o) {
1192
return o instanceof Entry<?, ?> entry
1193
&& removeMapping(entry.getKey(), entry.getValue());
1194
}
1195
public int size() {
1196
return size;
1197
}
1198
public void clear() {
1199
IdentityHashMap.this.clear();
1200
}
1201
/*
1202
* Must revert from AbstractSet's impl to AbstractCollection's, as
1203
* the former contains an optimization that results in incorrect
1204
* behavior when c is a smaller "normal" (non-identity-based) Set.
1205
*/
1206
public boolean removeAll(Collection<?> c) {
1207
Objects.requireNonNull(c);
1208
boolean modified = false;
1209
for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) {
1210
if (c.contains(i.next())) {
1211
i.remove();
1212
modified = true;
1213
}
1214
}
1215
return modified;
1216
}
1217
1218
public Object[] toArray() {
1219
return toArray(new Object[0]);
1220
}
1221
1222
@SuppressWarnings("unchecked")
1223
public <T> T[] toArray(T[] a) {
1224
int expectedModCount = modCount;
1225
int size = size();
1226
if (a.length < size)
1227
a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
1228
Object[] tab = table;
1229
int ti = 0;
1230
for (int si = 0; si < tab.length; si += 2) {
1231
Object key;
1232
if ((key = tab[si]) != null) { // key present ?
1233
// more elements than expected -> concurrent modification from other thread
1234
if (ti >= size) {
1235
throw new ConcurrentModificationException();
1236
}
1237
a[ti++] = (T) new AbstractMap.SimpleEntry<>(unmaskNull(key), tab[si + 1]);
1238
}
1239
}
1240
// fewer elements than expected or concurrent modification from other thread detected
1241
if (ti < size || expectedModCount != modCount) {
1242
throw new ConcurrentModificationException();
1243
}
1244
// final null marker as per spec
1245
if (ti < a.length) {
1246
a[ti] = null;
1247
}
1248
return a;
1249
}
1250
1251
public Spliterator<Map.Entry<K,V>> spliterator() {
1252
return new EntrySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0);
1253
}
1254
}
1255
1256
@java.io.Serial
1257
private static final long serialVersionUID = 8188218128353913216L;
1258
1259
/**
1260
* Saves the state of the {@code IdentityHashMap} instance to a stream
1261
* (i.e., serializes it).
1262
*
1263
* @serialData The <i>size</i> of the HashMap (the number of key-value
1264
* mappings) ({@code int}), followed by the key (Object) and
1265
* value (Object) for each key-value mapping represented by the
1266
* IdentityHashMap. The key-value mappings are emitted in no
1267
* particular order.
1268
*/
1269
@java.io.Serial
1270
private void writeObject(java.io.ObjectOutputStream s)
1271
throws java.io.IOException {
1272
// Write out and any hidden stuff
1273
s.defaultWriteObject();
1274
1275
// Write out size (number of Mappings)
1276
s.writeInt(size);
1277
1278
// Write out keys and values (alternating)
1279
Object[] tab = table;
1280
for (int i = 0; i < tab.length; i += 2) {
1281
Object key = tab[i];
1282
if (key != null) {
1283
s.writeObject(unmaskNull(key));
1284
s.writeObject(tab[i + 1]);
1285
}
1286
}
1287
}
1288
1289
/**
1290
* Reconstitutes the {@code IdentityHashMap} instance from a stream (i.e.,
1291
* deserializes it).
1292
*/
1293
@java.io.Serial
1294
private void readObject(java.io.ObjectInputStream s)
1295
throws java.io.IOException, ClassNotFoundException {
1296
// Read in any hidden stuff
1297
s.defaultReadObject();
1298
1299
// Read in size (number of Mappings)
1300
int size = s.readInt();
1301
if (size < 0)
1302
throw new java.io.StreamCorruptedException
1303
("Illegal mappings count: " + size);
1304
int cap = capacity(size);
1305
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, cap);
1306
init(cap);
1307
1308
// Read the keys and values, and put the mappings in the table
1309
for (int i=0; i<size; i++) {
1310
@SuppressWarnings("unchecked")
1311
K key = (K) s.readObject();
1312
@SuppressWarnings("unchecked")
1313
V value = (V) s.readObject();
1314
putForCreate(key, value);
1315
}
1316
}
1317
1318
/**
1319
* The put method for readObject. It does not resize the table,
1320
* update modCount, etc.
1321
*/
1322
private void putForCreate(K key, V value)
1323
throws java.io.StreamCorruptedException
1324
{
1325
Object k = maskNull(key);
1326
Object[] tab = table;
1327
int len = tab.length;
1328
int i = hash(k, len);
1329
1330
Object item;
1331
while ( (item = tab[i]) != null) {
1332
if (item == k)
1333
throw new java.io.StreamCorruptedException();
1334
i = nextKeyIndex(i, len);
1335
}
1336
tab[i] = k;
1337
tab[i + 1] = value;
1338
}
1339
1340
@SuppressWarnings("unchecked")
1341
@Override
1342
public void forEach(BiConsumer<? super K, ? super V> action) {
1343
Objects.requireNonNull(action);
1344
int expectedModCount = modCount;
1345
1346
Object[] t = table;
1347
for (int index = 0; index < t.length; index += 2) {
1348
Object k = t[index];
1349
if (k != null) {
1350
action.accept((K) unmaskNull(k), (V) t[index + 1]);
1351
}
1352
1353
if (modCount != expectedModCount) {
1354
throw new ConcurrentModificationException();
1355
}
1356
}
1357
}
1358
1359
@SuppressWarnings("unchecked")
1360
@Override
1361
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
1362
Objects.requireNonNull(function);
1363
int expectedModCount = modCount;
1364
1365
Object[] t = table;
1366
for (int index = 0; index < t.length; index += 2) {
1367
Object k = t[index];
1368
if (k != null) {
1369
t[index + 1] = function.apply((K) unmaskNull(k), (V) t[index + 1]);
1370
}
1371
1372
if (modCount != expectedModCount) {
1373
throw new ConcurrentModificationException();
1374
}
1375
}
1376
}
1377
1378
/**
1379
* Similar form as array-based Spliterators, but skips blank elements,
1380
* and guestimates size as decreasing by half per split.
1381
*/
1382
static class IdentityHashMapSpliterator<K,V> {
1383
final IdentityHashMap<K,V> map;
1384
int index; // current index, modified on advance/split
1385
int fence; // -1 until first use; then one past last index
1386
int est; // size estimate
1387
int expectedModCount; // initialized when fence set
1388
1389
IdentityHashMapSpliterator(IdentityHashMap<K,V> map, int origin,
1390
int fence, int est, int expectedModCount) {
1391
this.map = map;
1392
this.index = origin;
1393
this.fence = fence;
1394
this.est = est;
1395
this.expectedModCount = expectedModCount;
1396
}
1397
1398
final int getFence() { // initialize fence and size on first use
1399
int hi;
1400
if ((hi = fence) < 0) {
1401
est = map.size;
1402
expectedModCount = map.modCount;
1403
hi = fence = map.table.length;
1404
}
1405
return hi;
1406
}
1407
1408
public final long estimateSize() {
1409
getFence(); // force init
1410
return (long) est;
1411
}
1412
}
1413
1414
static final class KeySpliterator<K,V>
1415
extends IdentityHashMapSpliterator<K,V>
1416
implements Spliterator<K> {
1417
KeySpliterator(IdentityHashMap<K,V> map, int origin, int fence, int est,
1418
int expectedModCount) {
1419
super(map, origin, fence, est, expectedModCount);
1420
}
1421
1422
public KeySpliterator<K,V> trySplit() {
1423
int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
1424
return (lo >= mid) ? null :
1425
new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
1426
expectedModCount);
1427
}
1428
1429
@SuppressWarnings("unchecked")
1430
public void forEachRemaining(Consumer<? super K> action) {
1431
if (action == null)
1432
throw new NullPointerException();
1433
int i, hi, mc; Object key;
1434
IdentityHashMap<K,V> m; Object[] a;
1435
if ((m = map) != null && (a = m.table) != null &&
1436
(i = index) >= 0 && (index = hi = getFence()) <= a.length) {
1437
for (; i < hi; i += 2) {
1438
if ((key = a[i]) != null)
1439
action.accept((K)unmaskNull(key));
1440
}
1441
if (m.modCount == expectedModCount)
1442
return;
1443
}
1444
throw new ConcurrentModificationException();
1445
}
1446
1447
@SuppressWarnings("unchecked")
1448
public boolean tryAdvance(Consumer<? super K> action) {
1449
if (action == null)
1450
throw new NullPointerException();
1451
Object[] a = map.table;
1452
int hi = getFence();
1453
while (index < hi) {
1454
Object key = a[index];
1455
index += 2;
1456
if (key != null) {
1457
action.accept((K)unmaskNull(key));
1458
if (map.modCount != expectedModCount)
1459
throw new ConcurrentModificationException();
1460
return true;
1461
}
1462
}
1463
return false;
1464
}
1465
1466
public int characteristics() {
1467
return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;
1468
}
1469
}
1470
1471
static final class ValueSpliterator<K,V>
1472
extends IdentityHashMapSpliterator<K,V>
1473
implements Spliterator<V> {
1474
ValueSpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,
1475
int expectedModCount) {
1476
super(m, origin, fence, est, expectedModCount);
1477
}
1478
1479
public ValueSpliterator<K,V> trySplit() {
1480
int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
1481
return (lo >= mid) ? null :
1482
new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
1483
expectedModCount);
1484
}
1485
1486
public void forEachRemaining(Consumer<? super V> action) {
1487
if (action == null)
1488
throw new NullPointerException();
1489
int i, hi, mc;
1490
IdentityHashMap<K,V> m; Object[] a;
1491
if ((m = map) != null && (a = m.table) != null &&
1492
(i = index) >= 0 && (index = hi = getFence()) <= a.length) {
1493
for (; i < hi; i += 2) {
1494
if (a[i] != null) {
1495
@SuppressWarnings("unchecked") V v = (V)a[i+1];
1496
action.accept(v);
1497
}
1498
}
1499
if (m.modCount == expectedModCount)
1500
return;
1501
}
1502
throw new ConcurrentModificationException();
1503
}
1504
1505
public boolean tryAdvance(Consumer<? super V> action) {
1506
if (action == null)
1507
throw new NullPointerException();
1508
Object[] a = map.table;
1509
int hi = getFence();
1510
while (index < hi) {
1511
Object key = a[index];
1512
@SuppressWarnings("unchecked") V v = (V)a[index+1];
1513
index += 2;
1514
if (key != null) {
1515
action.accept(v);
1516
if (map.modCount != expectedModCount)
1517
throw new ConcurrentModificationException();
1518
return true;
1519
}
1520
}
1521
return false;
1522
}
1523
1524
public int characteristics() {
1525
return (fence < 0 || est == map.size ? SIZED : 0);
1526
}
1527
1528
}
1529
1530
static final class EntrySpliterator<K,V>
1531
extends IdentityHashMapSpliterator<K,V>
1532
implements Spliterator<Map.Entry<K,V>> {
1533
EntrySpliterator(IdentityHashMap<K,V> m, int origin, int fence, int est,
1534
int expectedModCount) {
1535
super(m, origin, fence, est, expectedModCount);
1536
}
1537
1538
public EntrySpliterator<K,V> trySplit() {
1539
int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1;
1540
return (lo >= mid) ? null :
1541
new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
1542
expectedModCount);
1543
}
1544
1545
public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {
1546
if (action == null)
1547
throw new NullPointerException();
1548
int i, hi, mc;
1549
IdentityHashMap<K,V> m; Object[] a;
1550
if ((m = map) != null && (a = m.table) != null &&
1551
(i = index) >= 0 && (index = hi = getFence()) <= a.length) {
1552
for (; i < hi; i += 2) {
1553
Object key = a[i];
1554
if (key != null) {
1555
@SuppressWarnings("unchecked") K k =
1556
(K)unmaskNull(key);
1557
@SuppressWarnings("unchecked") V v = (V)a[i+1];
1558
action.accept
1559
(new AbstractMap.SimpleImmutableEntry<>(k, v));
1560
1561
}
1562
}
1563
if (m.modCount == expectedModCount)
1564
return;
1565
}
1566
throw new ConcurrentModificationException();
1567
}
1568
1569
public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
1570
if (action == null)
1571
throw new NullPointerException();
1572
Object[] a = map.table;
1573
int hi = getFence();
1574
while (index < hi) {
1575
Object key = a[index];
1576
@SuppressWarnings("unchecked") V v = (V)a[index+1];
1577
index += 2;
1578
if (key != null) {
1579
@SuppressWarnings("unchecked") K k =
1580
(K)unmaskNull(key);
1581
action.accept
1582
(new AbstractMap.SimpleImmutableEntry<>(k, v));
1583
if (map.modCount != expectedModCount)
1584
throw new ConcurrentModificationException();
1585
return true;
1586
}
1587
}
1588
return false;
1589
}
1590
1591
public int characteristics() {
1592
return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT;
1593
}
1594
}
1595
1596
}
1597
1598