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}