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