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