001/* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.collect; 016 017import static com.google.common.base.Preconditions.checkArgument; 018import static com.google.common.base.Preconditions.checkNotNull; 019import static com.google.common.base.Preconditions.checkState; 020import static com.google.common.collect.CollectPreconditions.checkNonnegative; 021import static com.google.common.collect.Hashing.closedTableSize; 022import static com.google.common.collect.Hashing.smearedHash; 023import static java.util.Objects.requireNonNull; 024 025import com.google.common.annotations.GwtCompatible; 026import com.google.common.annotations.GwtIncompatible; 027import com.google.common.annotations.J2ktIncompatible; 028import com.google.common.annotations.VisibleForTesting; 029import com.google.common.collect.Maps.IteratorBasedAbstractMap; 030import com.google.errorprone.annotations.CanIgnoreReturnValue; 031import com.google.errorprone.annotations.concurrent.LazyInit; 032import com.google.j2objc.annotations.RetainedWith; 033import com.google.j2objc.annotations.Weak; 034import java.io.IOException; 035import java.io.InvalidObjectException; 036import java.io.ObjectInputStream; 037import java.io.ObjectOutputStream; 038import java.io.Serializable; 039import java.util.Arrays; 040import java.util.ConcurrentModificationException; 041import java.util.HashMap; 042import java.util.Iterator; 043import java.util.Map; 044import java.util.NoSuchElementException; 045import java.util.Objects; 046import java.util.Set; 047import java.util.function.BiConsumer; 048import java.util.function.BiFunction; 049import org.jspecify.annotations.Nullable; 050 051/** 052 * A {@link BiMap} backed by two hash tables. This implementation allows null keys and values. A 053 * {@code HashBiMap} and its inverse are both serializable. 054 * 055 * <p>This implementation guarantees insertion-based iteration order of its keys. 056 * 057 * <p>See the Guava User Guide article on <a href= 058 * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap">{@code BiMap} </a>. 059 * 060 * @author Louis Wasserman 061 * @author Mike Bostock 062 * @since 2.0 063 */ 064@GwtCompatible 065public final class HashBiMap<K extends @Nullable Object, V extends @Nullable Object> 066 extends IteratorBasedAbstractMap<K, V> implements BiMap<K, V>, Serializable { 067 068 /** Returns a new, empty {@code HashBiMap} with the default initial capacity (16). */ 069 public static <K extends @Nullable Object, V extends @Nullable Object> HashBiMap<K, V> create() { 070 return create(16); 071 } 072 073 /** 074 * Constructs a new, empty bimap with the specified expected size. 075 * 076 * @param expectedSize the expected number of entries 077 * @throws IllegalArgumentException if the specified expected size is negative 078 */ 079 public static <K extends @Nullable Object, V extends @Nullable Object> HashBiMap<K, V> create( 080 int expectedSize) { 081 return new HashBiMap<>(expectedSize); 082 } 083 084 /** 085 * Constructs a new bimap containing initial values from {@code map}. The bimap is created with an 086 * initial capacity sufficient to hold the mappings in the specified map. 087 */ 088 public static <K extends @Nullable Object, V extends @Nullable Object> HashBiMap<K, V> create( 089 Map<? extends K, ? extends V> map) { 090 HashBiMap<K, V> bimap = create(map.size()); 091 bimap.putAll(map); 092 return bimap; 093 } 094 095 /** 096 * An immutable key-value pair with mutable links to other pairs in its hash buckets and in 097 * iteration order. 098 */ 099 static final class Node<K extends @Nullable Object, V extends @Nullable Object> { 100 @ParametricNullness final K key; 101 @ParametricNullness final V value; 102 103 final int keyHash; 104 final int valueHash; 105 106 // All Node instances are strongly reachable from owning HashBiMap through 107 // "HashBiMap.hashTableKToV" and "Node.nextInKToVBucket" references. 108 // Under that assumption, the remaining references can be safely marked as @Weak. 109 // Using @Weak is necessary to avoid retain-cycles between Node instances on iOS, 110 // which would cause memory leaks when non-empty HashBiMap with cyclic Node 111 // instances is deallocated. 112 @Nullable Node<K, V> nextInKToVBucket; 113 @Weak @Nullable Node<K, V> nextInVToKBucket; 114 115 @Weak @Nullable Node<K, V> nextInKeyInsertionOrder; 116 @Weak @Nullable Node<K, V> prevInKeyInsertionOrder; 117 118 Node(@ParametricNullness K key, int keyHash, @ParametricNullness V value, int valueHash) { 119 this.key = key; 120 this.value = value; 121 this.keyHash = keyHash; 122 this.valueHash = valueHash; 123 } 124 } 125 126 private static final double LOAD_FACTOR = 1.0; 127 128 /** 129 * Maximum allowed false positive probability of detecting a hash flooding attack given random 130 * input. 131 */ 132 @VisibleForTesting( 133 ) 134 static final double HASH_FLOODING_FPP = 0.001; 135 136 /** 137 * Maximum allowed length of a hash table bucket before falling back to a j.u.HashMap-based 138 * implementation. Experimentally determined. 139 */ 140 private static final int MAX_HASH_BUCKET_LENGTH = 9; 141 142 static final class FloodedTables<K extends @Nullable Object, V extends @Nullable Object> { 143 final Map<K, Node<K, V>> kToV; 144 final Map<V, Node<K, V>> vToK; 145 146 FloodedTables(int tableSize) { 147 this.kToV = new HashMap<>(tableSize, 1.0f); 148 this.vToK = new HashMap<>(tableSize, 1.0f); 149 } 150 } 151 152 /* 153 * The following two arrays may *contain* nulls, but they are never *themselves* null: Even though 154 * they are not initialized inline in the constructor, they are initialized from init(), which the 155 * constructor calls (as does readObject()). 156 */ 157 @SuppressWarnings("nullness:initialization.field.uninitialized") // For J2KT (see above) 158 private transient @Nullable Node<K, V>[] hashTableKToV; 159 160 @SuppressWarnings("nullness:initialization.field.uninitialized") // For J2KT (see above) 161 private transient @Nullable Node<K, V>[] hashTableVToK; 162 163 private transient @Nullable FloodedTables<K, V> floodedTables; 164 165 @Weak private transient @Nullable Node<K, V> firstInKeyInsertionOrder; 166 @Weak private transient @Nullable Node<K, V> lastInKeyInsertionOrder; 167 private transient int size; 168 private transient int mask; 169 private transient int modCount; 170 171 private HashBiMap(int expectedSize) { 172 init(expectedSize); 173 } 174 175 private void init(int expectedSize) { 176 checkNonnegative(expectedSize, "expectedSize"); 177 int tableSize = closedTableSize(expectedSize, LOAD_FACTOR); 178 this.hashTableKToV = createTable(tableSize); 179 this.hashTableVToK = createTable(tableSize); 180 this.floodedTables = null; 181 this.firstInKeyInsertionOrder = null; 182 this.lastInKeyInsertionOrder = null; 183 this.size = 0; 184 this.mask = tableSize - 1; 185 this.modCount = 0; 186 } 187 188 @VisibleForTesting 189 boolean isUsingHashFloodingResistance() { 190 return floodedTables != null; 191 } 192 193 @VisibleForTesting 194 @CanIgnoreReturnValue 195 FloodedTables<K, V> convertToHashFloodingResistantImplementation() { 196 FloodedTables<K, V> tables = new FloodedTables<>(hashTableKToV.length); 197 for (Node<K, V> node = firstInKeyInsertionOrder; 198 node != null; 199 node = node.nextInKeyInsertionOrder) { 200 tables.kToV.put(node.key, node); 201 tables.vToK.put(node.value, node); 202 node.nextInKToVBucket = null; 203 node.nextInVToKBucket = null; 204 } 205 this.floodedTables = tables; 206 this.hashTableKToV = createTable(0); 207 this.hashTableVToK = createTable(0); 208 return tables; 209 } 210 211 /** 212 * Finds and removes {@code node} from the key-to-value hash table, the value-to-key hash table, 213 * and the iteration-order chain. This includes clearing its own references to other entries. 214 */ 215 private void delete(Node<K, V> node) { 216 if (floodedTables != null) { 217 floodedTables.kToV.remove(node.key); 218 floodedTables.vToK.remove(node.value); 219 } else { 220 int keyBucket = node.keyHash & mask; 221 Node<K, V> prevBucketNode = null; 222 for (Node<K, V> bucketNode = hashTableKToV[keyBucket]; 223 true; 224 bucketNode = bucketNode.nextInKToVBucket) { 225 if (bucketNode == node) { 226 if (prevBucketNode == null) { 227 hashTableKToV[keyBucket] = node.nextInKToVBucket; 228 } else { 229 prevBucketNode.nextInKToVBucket = node.nextInKToVBucket; 230 } 231 break; 232 } 233 prevBucketNode = bucketNode; 234 } 235 236 int valueBucket = node.valueHash & mask; 237 prevBucketNode = null; 238 for (Node<K, V> bucketNode = hashTableVToK[valueBucket]; 239 true; 240 bucketNode = bucketNode.nextInVToKBucket) { 241 if (bucketNode == node) { 242 if (prevBucketNode == null) { 243 hashTableVToK[valueBucket] = node.nextInVToKBucket; 244 } else { 245 prevBucketNode.nextInVToKBucket = node.nextInVToKBucket; 246 } 247 break; 248 } 249 prevBucketNode = bucketNode; 250 } 251 } 252 253 if (node.prevInKeyInsertionOrder == null) { 254 firstInKeyInsertionOrder = node.nextInKeyInsertionOrder; 255 } else { 256 node.prevInKeyInsertionOrder.nextInKeyInsertionOrder = node.nextInKeyInsertionOrder; 257 } 258 259 if (node.nextInKeyInsertionOrder == null) { 260 lastInKeyInsertionOrder = node.prevInKeyInsertionOrder; 261 } else { 262 node.nextInKeyInsertionOrder.prevInKeyInsertionOrder = node.prevInKeyInsertionOrder; 263 } 264 265 node.prevInKeyInsertionOrder = null; 266 node.nextInKeyInsertionOrder = null; 267 node.nextInKToVBucket = null; 268 node.nextInVToKBucket = null; 269 270 size--; 271 modCount++; 272 } 273 274 private void insertPlacingAtEndOfIterationOrder(Node<K, V> node) { 275 insertIntoHashBucketsOnly(node); 276 277 node.prevInKeyInsertionOrder = lastInKeyInsertionOrder; 278 if (lastInKeyInsertionOrder == null) { 279 firstInKeyInsertionOrder = node; 280 } else { 281 lastInKeyInsertionOrder.nextInKeyInsertionOrder = node; 282 } 283 lastInKeyInsertionOrder = node; 284 } 285 286 private void insertSplicingIntoIterationOrder( 287 Node<K, V> node, 288 @Nullable Node<K, V> prevInKeyInsertionOrder, 289 @Nullable Node<K, V> nextInKeyInsertionOrder) { 290 insertIntoHashBucketsOnly(node); 291 292 node.prevInKeyInsertionOrder = prevInKeyInsertionOrder; 293 if (prevInKeyInsertionOrder == null) { 294 firstInKeyInsertionOrder = node; 295 } else { 296 prevInKeyInsertionOrder.nextInKeyInsertionOrder = node; 297 } 298 299 node.nextInKeyInsertionOrder = nextInKeyInsertionOrder; 300 if (nextInKeyInsertionOrder == null) { 301 lastInKeyInsertionOrder = node; 302 } else { 303 nextInKeyInsertionOrder.prevInKeyInsertionOrder = node; 304 } 305 } 306 307 private void insertIntoHashBucketsOnly(Node<K, V> node) { 308 FloodedTables<K, V> tables = floodedTables; 309 if (tables == null) { 310 int keyBucket = node.keyHash & mask; 311 int valueBucket = node.valueHash & mask; 312 if (bucketLengthKToV(hashTableKToV[keyBucket]) >= MAX_HASH_BUCKET_LENGTH 313 || bucketLengthVToK(hashTableVToK[valueBucket]) >= MAX_HASH_BUCKET_LENGTH) { 314 tables = convertToHashFloodingResistantImplementation(); 315 } else { 316 node.nextInKToVBucket = hashTableKToV[keyBucket]; 317 hashTableKToV[keyBucket] = node; 318 319 node.nextInVToKBucket = hashTableVToK[valueBucket]; 320 hashTableVToK[valueBucket] = node; 321 322 size++; 323 modCount++; 324 return; 325 } 326 } 327 tables.kToV.put(node.key, node); 328 tables.vToK.put(node.value, node); 329 size++; 330 modCount++; 331 } 332 333 private static int bucketLengthKToV(@Nullable Node<?, ?> bucketHead) { 334 int length = 0; 335 for (Node<?, ?> node = bucketHead; node != null; node = node.nextInKToVBucket) { 336 length++; 337 } 338 return length; 339 } 340 341 private static int bucketLengthVToK(@Nullable Node<?, ?> bucketHead) { 342 int length = 0; 343 for (Node<?, ?> node = bucketHead; node != null; node = node.nextInVToKBucket) { 344 length++; 345 } 346 return length; 347 } 348 349 private void replaceNodeForKey(Node<K, V> oldNode, Node<K, V> newNode) { 350 Node<K, V> prevInKeyInsertionOrder = oldNode.prevInKeyInsertionOrder; 351 Node<K, V> nextInKeyInsertionOrder = oldNode.nextInKeyInsertionOrder; 352 delete(oldNode); // clears the two fields we just read 353 insertSplicingIntoIterationOrder(newNode, prevInKeyInsertionOrder, nextInKeyInsertionOrder); 354 } 355 356 private @Nullable Node<K, V> seekByKey(@Nullable Object key, int keyHash) { 357 if (floodedTables != null) { 358 return floodedTables.kToV.get(key); 359 } 360 for (Node<K, V> node = hashTableKToV[keyHash & mask]; 361 node != null; 362 node = node.nextInKToVBucket) { 363 if (keyHash == node.keyHash && Objects.equals(key, node.key)) { 364 return node; 365 } 366 } 367 return null; 368 } 369 370 private @Nullable Node<K, V> seekByValue(@Nullable Object value, int valueHash) { 371 if (floodedTables != null) { 372 return floodedTables.vToK.get(value); 373 } 374 for (Node<K, V> node = hashTableVToK[valueHash & mask]; 375 node != null; 376 node = node.nextInVToKBucket) { 377 if (valueHash == node.valueHash && Objects.equals(value, node.value)) { 378 return node; 379 } 380 } 381 return null; 382 } 383 384 @Override 385 public boolean containsKey(@Nullable Object key) { 386 return seekByKey(key, smearedHash(key)) != null; 387 } 388 389 /** 390 * Returns {@code true} if this BiMap contains an entry whose value is equal to {@code value} (or, 391 * equivalently, if this inverse view contains a key that is equal to {@code value}). 392 * 393 * <p>Due to the property that values in a BiMap are unique, this will tend to execute in 394 * faster-than-linear time. 395 * 396 * @param value the object to search for in the values of this BiMap 397 * @return true if a mapping exists from a key to the specified value 398 */ 399 @Override 400 public boolean containsValue(@Nullable Object value) { 401 return seekByValue(value, smearedHash(value)) != null; 402 } 403 404 @Override 405 public @Nullable V get(@Nullable Object key) { 406 return valueOrNull(seekByKey(key, smearedHash(key))); 407 } 408 409 @CanIgnoreReturnValue 410 @Override 411 public @Nullable V put(@ParametricNullness K key, @ParametricNullness V value) { 412 return put(key, value, false); 413 } 414 415 private @Nullable V put(@ParametricNullness K key, @ParametricNullness V value, boolean force) { 416 int keyHash = smearedHash(key); 417 int valueHash = smearedHash(value); 418 419 Node<K, V> oldNodeForKey = seekByKey(key, keyHash); 420 if (oldNodeForKey != null 421 && valueHash == oldNodeForKey.valueHash 422 && Objects.equals(value, oldNodeForKey.value)) { 423 return value; 424 } 425 426 Node<K, V> oldNodeForValue = seekByValue(value, valueHash); 427 if (oldNodeForValue != null) { 428 if (force) { 429 delete(oldNodeForValue); 430 } else { 431 throw new IllegalArgumentException("value already present: " + value); 432 } 433 } 434 435 Node<K, V> newNode = new Node<>(key, keyHash, value, valueHash); 436 if (oldNodeForKey != null) { 437 replaceNodeForKey(oldNodeForKey, newNode); 438 return oldNodeForKey.value; 439 } else { 440 insertPlacingAtEndOfIterationOrder(newNode); 441 rehashIfNecessary(); 442 return null; 443 } 444 } 445 446 @CanIgnoreReturnValue 447 @Override 448 public @Nullable V forcePut(@ParametricNullness K key, @ParametricNullness V value) { 449 return put(key, value, true); 450 } 451 452 @CanIgnoreReturnValue 453 private @Nullable K putInverse( 454 @ParametricNullness V value, @ParametricNullness K key, boolean force) { 455 int valueHash = smearedHash(value); 456 int keyHash = smearedHash(key); 457 458 Node<K, V> oldNodeForValue = seekByValue(value, valueHash); 459 Node<K, V> oldNodeForKey = seekByKey(key, keyHash); 460 if (oldNodeForValue != null 461 && keyHash == oldNodeForValue.keyHash 462 && Objects.equals(key, oldNodeForValue.key)) { 463 return key; 464 } else if (oldNodeForKey != null && !force) { 465 throw new IllegalArgumentException("key already present: " + key); 466 } 467 468 if (oldNodeForValue != null) { 469 delete(oldNodeForValue); 470 } 471 472 Node<K, V> newNode = new Node<>(key, keyHash, value, valueHash); 473 if (oldNodeForKey != null) { 474 replaceNodeForKey(oldNodeForKey, newNode); 475 } else { 476 insertPlacingAtEndOfIterationOrder(newNode); 477 rehashIfNecessary(); 478 } 479 return keyOrNull(oldNodeForValue); 480 } 481 482 private void rehashIfNecessary() { 483 if (floodedTables != null) { 484 return; 485 } 486 @Nullable Node<K, V>[] oldKToV = hashTableKToV; 487 if (Hashing.needsResizing(size, oldKToV.length, LOAD_FACTOR)) { 488 int newTableSize = oldKToV.length * 2; 489 490 this.hashTableKToV = createTable(newTableSize); 491 this.hashTableVToK = createTable(newTableSize); 492 this.mask = newTableSize - 1; 493 this.size = 0; 494 495 Node<K, V> oldFirst = firstInKeyInsertionOrder; 496 this.firstInKeyInsertionOrder = null; 497 this.lastInKeyInsertionOrder = null; 498 reinsertNodes(oldFirst); 499 this.modCount++; 500 } 501 } 502 503 @SuppressWarnings({"unchecked", "rawtypes"}) 504 private @Nullable Node<K, V>[] createTable(int length) { 505 return new @Nullable Node[length]; 506 } 507 508 @CanIgnoreReturnValue 509 @Override 510 public @Nullable V remove(@Nullable Object key) { 511 Node<K, V> node = seekByKey(key, smearedHash(key)); 512 if (node == null) { 513 return null; 514 } else { 515 delete(node); 516 return node.value; 517 } 518 } 519 520 @Override 521 public void clear() { 522 size = 0; 523 if (floodedTables != null) { 524 floodedTables.kToV.clear(); 525 floodedTables.vToK.clear(); 526 } else { 527 Arrays.fill(hashTableKToV, null); 528 Arrays.fill(hashTableVToK, null); 529 } 530 firstInKeyInsertionOrder = null; 531 lastInKeyInsertionOrder = null; 532 modCount++; 533 } 534 535 @Override 536 public int size() { 537 return size; 538 } 539 540 private abstract static class BiIterator< 541 K extends @Nullable Object, V extends @Nullable Object, T extends @Nullable Object> 542 implements Iterator<T> { 543 final HashBiMap<K, V> biMap; 544 545 @Nullable Node<K, V> next; 546 @Nullable Node<K, V> toRemove; 547 int expectedModCount; 548 int remaining; 549 550 BiIterator(HashBiMap<K, V> biMap) { 551 this.biMap = biMap; 552 next = biMap.firstInKeyInsertionOrder; 553 expectedModCount = biMap.modCount; 554 remaining = biMap.size(); 555 } 556 557 @Override 558 public final boolean hasNext() { 559 if (biMap.modCount != expectedModCount) { 560 throw new ConcurrentModificationException(); 561 } 562 return next != null && remaining > 0; 563 } 564 565 @Override 566 public final T next() { 567 if (!hasNext()) { 568 throw new NoSuchElementException(); 569 } 570 571 // requireNonNull is safe because of the hasNext check. 572 Node<K, V> node = requireNonNull(next); 573 next = node.nextInKeyInsertionOrder; 574 toRemove = node; 575 remaining--; 576 return output(node); 577 } 578 579 @Override 580 public final void remove() { 581 if (biMap.modCount != expectedModCount) { 582 throw new ConcurrentModificationException(); 583 } 584 if (toRemove == null) { 585 throw new IllegalStateException("no calls to next() since the last call to remove()"); 586 } 587 biMap.delete(toRemove); 588 expectedModCount = biMap.modCount; 589 toRemove = null; 590 } 591 592 abstract T output(Node<K, V> node); 593 } 594 595 @Override 596 public Set<K> keySet() { 597 return new KeySet(); 598 } 599 600 private final class KeySet extends Maps.KeySet<K, V> { 601 KeySet() { 602 super(HashBiMap.this); 603 } 604 605 @Override 606 public Iterator<K> iterator() { 607 return new BiIterator<K, V, K>(HashBiMap.this) { 608 @Override 609 @ParametricNullness 610 K output(Node<K, V> node) { 611 return node.key; 612 } 613 }; 614 } 615 616 @Override 617 public boolean remove(@Nullable Object o) { 618 Node<K, V> node = seekByKey(o, smearedHash(o)); 619 if (node == null) { 620 return false; 621 } else { 622 delete(node); 623 return true; 624 } 625 } 626 } 627 628 @Override 629 public Set<V> values() { 630 return inverse().keySet(); 631 } 632 633 @Override 634 Iterator<Entry<K, V>> entryIterator() { 635 return new BiIterator<K, V, Entry<K, V>>(HashBiMap.this) { 636 @Override 637 Entry<K, V> output(Node<K, V> node) { 638 return new MapEntry(node); 639 } 640 641 final class MapEntry extends AbstractMapEntry<K, V> { 642 private Node<K, V> node; 643 644 MapEntry(Node<K, V> node) { 645 this.node = node; 646 } 647 648 @Override 649 @ParametricNullness 650 public K getKey() { 651 return node.key; 652 } 653 654 @Override 655 @ParametricNullness 656 public V getValue() { 657 return node.value; 658 } 659 660 @Override 661 @ParametricNullness 662 public V setValue(@ParametricNullness V value) { 663 checkState(seekByKey(node.key, node.keyHash) == node, "entry no longer in map"); 664 V oldValue = node.value; 665 int valueHash = smearedHash(value); 666 if (valueHash == node.valueHash && Objects.equals(value, oldValue)) { 667 return value; 668 } 669 checkArgument(seekByValue(value, valueHash) == null, "value already present: %s", value); 670 Node<K, V> newNode = new Node<>(node.key, node.keyHash, value, valueHash); 671 replaceNodeForKey(node, newNode); 672 expectedModCount = modCount; 673 if (Objects.equals(toRemove, node)) { 674 toRemove = newNode; 675 } 676 node = newNode; 677 return oldValue; 678 } 679 } 680 }; 681 } 682 683 @Override 684 public void forEach(BiConsumer<? super K, ? super V> action) { 685 checkNotNull(action); 686 for (Node<K, V> node = firstInKeyInsertionOrder; 687 node != null; 688 node = node.nextInKeyInsertionOrder) { 689 action.accept(node.key, node.value); 690 } 691 } 692 693 @SuppressWarnings("CatchingUnchecked") // sneaky checked exception 694 @Override 695 public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { 696 checkNotNull(function); 697 Node<K, V> oldFirst = firstInKeyInsertionOrder; 698 clear(); 699 try { 700 for (Node<K, V> node = oldFirst; node != null; node = node.nextInKeyInsertionOrder) { 701 put(node.key, function.apply(node.key, node.value)); 702 } 703 } catch (Exception e) { // sneaky checked exception 704 restoreFromSavedNodes(oldFirst); 705 throw e; 706 } 707 } 708 709 private void restoreFromSavedNodes(@Nullable Node<K, V> oldFirst) { 710 clear(); 711 reinsertNodes(oldFirst); 712 } 713 714 private void reinsertNodes(@Nullable Node<K, V> oldFirst) { 715 for (Node<K, V> node = oldFirst; node != null; ) { 716 Node<K, V> next = node.nextInKeyInsertionOrder; 717 node.prevInKeyInsertionOrder = null; 718 node.nextInKeyInsertionOrder = null; 719 node.nextInKToVBucket = null; 720 node.nextInVToKBucket = null; 721 insertPlacingAtEndOfIterationOrder(node); 722 node = next; 723 } 724 } 725 726 @LazyInit @RetainedWith private transient @Nullable BiMap<V, K> inverse; 727 728 @Override 729 public BiMap<V, K> inverse() { 730 BiMap<V, K> result = inverse; 731 if (result == null) { 732 result = inverse = new Inverse<>(this); 733 } 734 return result; 735 } 736 737 private static final class Inverse<K extends @Nullable Object, V extends @Nullable Object> 738 extends IteratorBasedAbstractMap<V, K> implements BiMap<V, K>, Serializable { 739 final HashBiMap<K, V> obverse; 740 741 Inverse(HashBiMap<K, V> obverse) { 742 this.obverse = obverse; 743 } 744 745 @Override 746 public int size() { 747 return obverse.size; 748 } 749 750 @Override 751 public void clear() { 752 obverse.clear(); 753 } 754 755 @Override 756 public boolean containsKey(@Nullable Object key) { 757 Object obverseValue = key; 758 return obverse.containsValue(obverseValue); 759 } 760 761 @Override 762 public @Nullable K get(@Nullable Object key) { 763 Object obverseValue = key; 764 return keyOrNull(obverse.seekByValue(obverseValue, smearedHash(obverseValue))); 765 } 766 767 @CanIgnoreReturnValue 768 @Override 769 public @Nullable K put(@ParametricNullness V key, @ParametricNullness K value) { 770 K obverseKey = value; 771 V obverseValue = key; 772 return obverse.putInverse(obverseValue, obverseKey, false); 773 } 774 775 @CanIgnoreReturnValue 776 @Override 777 public @Nullable K forcePut(@ParametricNullness V key, @ParametricNullness K value) { 778 K obverseKey = value; 779 V obverseValue = key; 780 return obverse.putInverse(obverseValue, obverseKey, true); 781 } 782 783 @CanIgnoreReturnValue 784 @Override 785 public @Nullable K remove(@Nullable Object key) { 786 Object obverseValue = key; 787 Node<K, V> node = obverse.seekByValue(obverseValue, smearedHash(obverseValue)); 788 if (node == null) { 789 return null; 790 } 791 obverse.delete(node); 792 return node.key; 793 } 794 795 @Override 796 public BiMap<K, V> inverse() { 797 return obverse; 798 } 799 800 @Override 801 public Set<V> keySet() { 802 return new InverseKeySet(); 803 } 804 805 private final class InverseKeySet extends Maps.KeySet<V, K> { 806 InverseKeySet() { 807 super(Inverse.this); 808 } 809 810 @Override 811 public boolean remove(@Nullable Object o) { 812 Node<K, V> node = obverse.seekByValue(o, smearedHash(o)); 813 if (node == null) { 814 return false; 815 } else { 816 obverse.delete(node); 817 return true; 818 } 819 } 820 821 @Override 822 public Iterator<V> iterator() { 823 return new BiIterator<K, V, V>(obverse) { 824 @Override 825 @ParametricNullness 826 V output(Node<K, V> node) { 827 return node.value; 828 } 829 }; 830 } 831 } 832 833 @Override 834 public Set<K> values() { 835 return obverse.keySet(); 836 } 837 838 @Override 839 Iterator<Entry<V, K>> entryIterator() { 840 return new BiIterator<K, V, Entry<V, K>>(obverse) { 841 @Override 842 Entry<V, K> output(Node<K, V> node) { 843 return new InverseEntry(node); 844 } 845 846 final class InverseEntry extends AbstractMapEntry<V, K> { 847 private Node<K, V> node; 848 849 InverseEntry(Node<K, V> node) { 850 this.node = node; 851 } 852 853 @Override 854 @ParametricNullness 855 public V getKey() { 856 return node.value; 857 } 858 859 @Override 860 @ParametricNullness 861 public K getValue() { 862 return node.key; 863 } 864 865 @Override 866 @ParametricNullness 867 public K setValue(@ParametricNullness K value) { 868 K obverseKey = value; 869 return setObverseKey(obverseKey); 870 } 871 872 @ParametricNullness 873 private K setObverseKey(@ParametricNullness K obverseKey) { 874 checkState(obverse.seekByKey(node.key, node.keyHash) == node, "entry no longer in map"); 875 int obverseKeyHash = smearedHash(obverseKey); 876 if (obverseKeyHash == node.keyHash && Objects.equals(obverseKey, node.key)) { 877 return obverseKey; 878 } 879 checkArgument( 880 obverse.seekByKey(obverseKey, obverseKeyHash) == null, 881 "value already present: %s", 882 obverseKey); 883 obverse.delete(node); 884 Node<K, V> newNode = new Node<>(obverseKey, obverseKeyHash, node.value, node.valueHash); 885 obverse.insertPlacingAtEndOfIterationOrder(newNode); 886 expectedModCount = obverse.modCount; 887 K oldObverseKey = node.key; 888 if (Objects.equals(toRemove, node)) { 889 toRemove = newNode; 890 } 891 node = newNode; 892 return oldObverseKey; 893 } 894 } 895 }; 896 } 897 898 @Override 899 public void forEach(BiConsumer<? super V, ? super K> action) { 900 checkNotNull(action); 901 obverse.forEach((k, v) -> action.accept(v, k)); 902 } 903 904 @SuppressWarnings("CatchingUnchecked") // sneaky checked exception 905 @Override 906 public void replaceAll(BiFunction<? super V, ? super K, ? extends K> function) { 907 checkNotNull(function); 908 Node<K, V> oldFirst = obverse.firstInKeyInsertionOrder; 909 clear(); 910 try { 911 for (Node<K, V> node = oldFirst; node != null; node = node.nextInKeyInsertionOrder) { 912 put(node.value, function.apply(node.value, node.key)); 913 } 914 } catch (Exception e) { // sneaky checked exception 915 obverse.restoreFromSavedNodes(oldFirst); 916 throw e; 917 } 918 } 919 920 Object writeReplace() { 921 return new InverseSerializedForm<>(obverse); 922 } 923 924 @GwtIncompatible 925 @J2ktIncompatible 926 private void readObject(ObjectInputStream in) throws InvalidObjectException { 927 throw new InvalidObjectException("Use InverseSerializedForm"); 928 } 929 } 930 931 private static <K extends @Nullable Object> @Nullable K keyOrNull(@Nullable Node<K, ?> node) { 932 return node == null ? null : node.key; 933 } 934 935 private static <V extends @Nullable Object> @Nullable V valueOrNull(@Nullable Node<?, V> node) { 936 return node == null ? null : node.value; 937 } 938 939 private static final class InverseSerializedForm< 940 K extends @Nullable Object, V extends @Nullable Object> 941 implements Serializable { 942 private final HashBiMap<K, V> bimap; 943 944 InverseSerializedForm(HashBiMap<K, V> bimap) { 945 this.bimap = bimap; 946 } 947 948 Object readResolve() { 949 return bimap.inverse(); 950 } 951 952 @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 2881074110665323944L; 953 } 954 955 /** 956 * @serialData the number of entries, first key, first value, second key, second value, and so on. 957 */ 958 @GwtIncompatible 959 @J2ktIncompatible 960 private void writeObject(ObjectOutputStream stream) throws IOException { 961 stream.defaultWriteObject(); 962 Serialization.writeMap(this, stream); 963 } 964 965 @GwtIncompatible 966 @J2ktIncompatible 967 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 968 stream.defaultReadObject(); 969 int size = stream.readInt(); 970 init(16); // resist hostile attempts to allocate gratuitous heap 971 Serialization.populateMap(this, stream, size); 972 } 973 974 @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; 975}