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.collect.NullnessCasts.uncheckedCastNullableTToT;
019import static com.google.common.collect.NullnessCasts.unsafeNull;
020
021import com.google.common.annotations.GwtCompatible;
022import com.google.common.annotations.GwtIncompatible;
023import com.google.common.annotations.J2ktIncompatible;
024import com.google.common.base.Objects;
025import com.google.errorprone.annotations.CanIgnoreReturnValue;
026import com.google.errorprone.annotations.concurrent.LazyInit;
027import com.google.j2objc.annotations.RetainedWith;
028import java.io.IOException;
029import java.io.ObjectInputStream;
030import java.io.ObjectOutputStream;
031import java.io.Serializable;
032import java.util.AbstractMap;
033import java.util.AbstractSet;
034import java.util.Arrays;
035import java.util.ConcurrentModificationException;
036import java.util.Iterator;
037import java.util.Map;
038import java.util.NoSuchElementException;
039import java.util.Set;
040import org.jspecify.annotations.Nullable;
041
042/**
043 * A {@link BiMap} backed by two hash tables. This implementation allows null keys and values. A
044 * {@code HashBiMap} and its inverse are both serializable.
045 *
046 * <p>This implementation guarantees insertion-based iteration order of its keys.
047 *
048 * <p>See the Guava User Guide article on <a href=
049 * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap">{@code BiMap} </a>.
050 *
051 * @author Louis Wasserman
052 * @author Mike Bostock
053 * @since 2.0
054 */
055@GwtCompatible
056public final class HashBiMap<K extends @Nullable Object, V extends @Nullable Object>
057    extends AbstractMap<K, V> implements BiMap<K, V>, Serializable {
058
059  /** Returns a new, empty {@code HashBiMap} with the default initial capacity (16). */
060  public static <K extends @Nullable Object, V extends @Nullable Object> HashBiMap<K, V> create() {
061    return create(16);
062  }
063
064  /**
065   * Constructs a new, empty bimap with the specified expected size.
066   *
067   * @param expectedSize the expected number of entries
068   * @throws IllegalArgumentException if the specified expected size is negative
069   */
070  public static <K extends @Nullable Object, V extends @Nullable Object> HashBiMap<K, V> create(
071      int expectedSize) {
072    return new HashBiMap<>(expectedSize);
073  }
074
075  /**
076   * Constructs a new bimap containing initial values from {@code map}. The bimap is created with an
077   * initial capacity sufficient to hold the mappings in the specified map.
078   */
079  public static <K extends @Nullable Object, V extends @Nullable Object> HashBiMap<K, V> create(
080      Map<? extends K, ? extends V> map) {
081    HashBiMap<K, V> bimap = create(map.size());
082    bimap.putAll(map);
083    return bimap;
084  }
085
086  private static final int ABSENT = -1;
087  private static final int ENDPOINT = -2;
088
089  /** Maps an "entry" to the key of that entry. */
090  transient @Nullable K[] keys;
091
092  /** Maps an "entry" to the value of that entry. */
093  transient @Nullable V[] values;
094
095  transient int size;
096  transient int modCount;
097
098  /** Maps a bucket to the "entry" of its first element. */
099  private transient int[] hashTableKToV;
100
101  /** Maps a bucket to the "entry" of its first element. */
102  private transient int[] hashTableVToK;
103
104  /** Maps an "entry" to the "entry" that follows it in its bucket. */
105  private transient int[] nextInBucketKToV;
106
107  /** Maps an "entry" to the "entry" that follows it in its bucket. */
108  private transient int[] nextInBucketVToK;
109
110  /** The "entry" of the first element in insertion order. */
111  private transient int firstInInsertionOrder;
112
113  /** The "entry" of the last element in insertion order. */
114  private transient int lastInInsertionOrder;
115
116  /** Maps an "entry" to the "entry" that precedes it in insertion order. */
117  private transient int[] prevInInsertionOrder;
118
119  /** Maps an "entry" to the "entry" that follows it in insertion order. */
120  private transient int[] nextInInsertionOrder;
121
122  private HashBiMap(int expectedSize) {
123    init(expectedSize);
124  }
125
126  @SuppressWarnings("unchecked")
127  void init(int expectedSize) {
128    CollectPreconditions.checkNonnegative(expectedSize, "expectedSize");
129    int tableSize = Hashing.closedTableSize(expectedSize, 1.0);
130    size = 0;
131
132    keys = (K[]) new Object[expectedSize];
133    values = (V[]) new Object[expectedSize];
134
135    hashTableKToV = createFilledWithAbsent(tableSize);
136    hashTableVToK = createFilledWithAbsent(tableSize);
137    nextInBucketKToV = createFilledWithAbsent(expectedSize);
138    nextInBucketVToK = createFilledWithAbsent(expectedSize);
139
140    firstInInsertionOrder = ENDPOINT;
141    lastInInsertionOrder = ENDPOINT;
142
143    prevInInsertionOrder = createFilledWithAbsent(expectedSize);
144    nextInInsertionOrder = createFilledWithAbsent(expectedSize);
145  }
146
147  /** Returns an int array of the specified size, filled with ABSENT. */
148  private static int[] createFilledWithAbsent(int size) {
149    int[] array = new int[size];
150    Arrays.fill(array, ABSENT);
151    return array;
152  }
153
154  /** Equivalent to {@code Arrays.copyOf(array, newSize)}, save that the new elements are ABSENT. */
155  private static int[] expandAndFillWithAbsent(int[] array, int newSize) {
156    int oldSize = array.length;
157    int[] result = Arrays.copyOf(array, newSize);
158    Arrays.fill(result, oldSize, newSize, ABSENT);
159    return result;
160  }
161
162  @Override
163  public int size() {
164    return size;
165  }
166
167  /**
168   * Ensures that all of the internal structures in the HashBiMap are ready for this many elements.
169   */
170  private void ensureCapacity(int minCapacity) {
171    if (nextInBucketKToV.length < minCapacity) {
172      int oldCapacity = nextInBucketKToV.length;
173      int newCapacity = ImmutableCollection.Builder.expandedCapacity(oldCapacity, minCapacity);
174
175      keys = Arrays.copyOf(keys, newCapacity);
176      values = Arrays.copyOf(values, newCapacity);
177      nextInBucketKToV = expandAndFillWithAbsent(nextInBucketKToV, newCapacity);
178      nextInBucketVToK = expandAndFillWithAbsent(nextInBucketVToK, newCapacity);
179      prevInInsertionOrder = expandAndFillWithAbsent(prevInInsertionOrder, newCapacity);
180      nextInInsertionOrder = expandAndFillWithAbsent(nextInInsertionOrder, newCapacity);
181    }
182
183    if (hashTableKToV.length < minCapacity) {
184      int newTableSize = Hashing.closedTableSize(minCapacity, 1.0);
185      hashTableKToV = createFilledWithAbsent(newTableSize);
186      hashTableVToK = createFilledWithAbsent(newTableSize);
187
188      for (int entryToRehash = 0; entryToRehash < size; entryToRehash++) {
189        int keyHash = Hashing.smearedHash(keys[entryToRehash]);
190        int keyBucket = bucket(keyHash);
191        nextInBucketKToV[entryToRehash] = hashTableKToV[keyBucket];
192        hashTableKToV[keyBucket] = entryToRehash;
193
194        int valueHash = Hashing.smearedHash(values[entryToRehash]);
195        int valueBucket = bucket(valueHash);
196        nextInBucketVToK[entryToRehash] = hashTableVToK[valueBucket];
197        hashTableVToK[valueBucket] = entryToRehash;
198      }
199    }
200  }
201
202  /**
203   * Returns the bucket (in either the K-to-V or V-to-K tables) where elements with the specified
204   * hash could be found, if present, or could be inserted.
205   */
206  private int bucket(int hash) {
207    return hash & (hashTableKToV.length - 1);
208  }
209
210  /** Given a key, returns the index of the entry in the tables, or ABSENT if not found. */
211  int findEntryByKey(@Nullable Object key) {
212    return findEntryByKey(key, Hashing.smearedHash(key));
213  }
214
215  /**
216   * Given a key and its hash, returns the index of the entry in the tables, or ABSENT if not found.
217   */
218  int findEntryByKey(@Nullable Object key, int keyHash) {
219    return findEntry(key, keyHash, hashTableKToV, nextInBucketKToV, keys);
220  }
221
222  /** Given a value, returns the index of the entry in the tables, or ABSENT if not found. */
223  int findEntryByValue(@Nullable Object value) {
224    return findEntryByValue(value, Hashing.smearedHash(value));
225  }
226
227  /**
228   * Given a value and its hash, returns the index of the entry in the tables, or ABSENT if not
229   * found.
230   */
231  int findEntryByValue(@Nullable Object value, int valueHash) {
232    return findEntry(value, valueHash, hashTableVToK, nextInBucketVToK, values);
233  }
234
235  int findEntry(
236      @Nullable Object o,
237      int oHash,
238      int[] hashTable,
239      int[] nextInBucket,
240      @Nullable Object[] array) {
241    for (int entry = hashTable[bucket(oHash)]; entry != ABSENT; entry = nextInBucket[entry]) {
242      if (Objects.equal(array[entry], o)) {
243        return entry;
244      }
245    }
246    return ABSENT;
247  }
248
249  @Override
250  public boolean containsKey(@Nullable Object key) {
251    return findEntryByKey(key) != ABSENT;
252  }
253
254  /**
255   * Returns {@code true} if this BiMap contains an entry whose value is equal to {@code value} (or,
256   * equivalently, if this inverse view contains a key that is equal to {@code value}).
257   *
258   * <p>Due to the property that values in a BiMap are unique, this will tend to execute in
259   * faster-than-linear time.
260   *
261   * @param value the object to search for in the values of this BiMap
262   * @return true if a mapping exists from a key to the specified value
263   */
264  @Override
265  public boolean containsValue(@Nullable Object value) {
266    return findEntryByValue(value) != ABSENT;
267  }
268
269  @Override
270  public @Nullable V get(@Nullable Object key) {
271    int entry = findEntryByKey(key);
272    return (entry == ABSENT) ? null : values[entry];
273  }
274
275  @Nullable K getInverse(@Nullable Object value) {
276    int entry = findEntryByValue(value);
277    return (entry == ABSENT) ? null : keys[entry];
278  }
279
280  @Override
281  @CanIgnoreReturnValue
282  public @Nullable V put(@ParametricNullness K key, @ParametricNullness V value) {
283    return put(key, value, false);
284  }
285
286  @Nullable V put(@ParametricNullness K key, @ParametricNullness V value, boolean force) {
287    int keyHash = Hashing.smearedHash(key);
288    int entryForKey = findEntryByKey(key, keyHash);
289    if (entryForKey != ABSENT) {
290      V oldValue = values[entryForKey];
291      if (Objects.equal(oldValue, value)) {
292        return value;
293      } else {
294        replaceValueInEntry(entryForKey, value, force);
295        return oldValue;
296      }
297    }
298
299    int valueHash = Hashing.smearedHash(value);
300    int valueEntry = findEntryByValue(value, valueHash);
301    if (force) {
302      if (valueEntry != ABSENT) {
303        removeEntryValueHashKnown(valueEntry, valueHash);
304      }
305    } else {
306      checkArgument(valueEntry == ABSENT, "Value already present: %s", value);
307    }
308
309    ensureCapacity(size + 1);
310    keys[size] = key;
311    values[size] = value;
312
313    insertIntoTableKToV(size, keyHash);
314    insertIntoTableVToK(size, valueHash);
315
316    setSucceeds(lastInInsertionOrder, size);
317    setSucceeds(size, ENDPOINT);
318    size++;
319    modCount++;
320    return null;
321  }
322
323  @Override
324  @CanIgnoreReturnValue
325  public @Nullable V forcePut(@ParametricNullness K key, @ParametricNullness V value) {
326    return put(key, value, true);
327  }
328
329  @CanIgnoreReturnValue
330  @Nullable K putInverse(@ParametricNullness V value, @ParametricNullness K key, boolean force) {
331    int valueHash = Hashing.smearedHash(value);
332    int entryForValue = findEntryByValue(value, valueHash);
333    if (entryForValue != ABSENT) {
334      K oldKey = keys[entryForValue];
335      if (Objects.equal(oldKey, key)) {
336        return key;
337      } else {
338        replaceKeyInEntry(entryForValue, key, force);
339        return oldKey;
340      }
341    }
342
343    int predecessor = lastInInsertionOrder;
344    int keyHash = Hashing.smearedHash(key);
345    int keyEntry = findEntryByKey(key, keyHash);
346    if (force) {
347      if (keyEntry != ABSENT) {
348        predecessor = prevInInsertionOrder[keyEntry];
349        removeEntryKeyHashKnown(keyEntry, keyHash);
350      }
351    } else {
352      checkArgument(keyEntry == ABSENT, "Key already present: %s", key);
353    }
354
355    // insertion point for new entry is after predecessor
356    // note predecessor must still be a valid entry: either we deleted an entry that was *not*
357    // predecessor, or we didn't delete anything
358
359    ensureCapacity(size + 1);
360    keys[size] = key;
361    values[size] = value;
362
363    insertIntoTableKToV(size, keyHash);
364    insertIntoTableVToK(size, valueHash);
365
366    int successor =
367        (predecessor == ENDPOINT) ? firstInInsertionOrder : nextInInsertionOrder[predecessor];
368    setSucceeds(predecessor, size);
369    setSucceeds(size, successor);
370    size++;
371    modCount++;
372    return null;
373  }
374
375  /**
376   * Updates the pointers of the insertion order linked list so that {@code next} follows {@code
377   * prev}. {@code ENDPOINT} represents either the first or last entry in the entire map (as
378   * appropriate).
379   */
380  private void setSucceeds(int prev, int next) {
381    if (prev == ENDPOINT) {
382      firstInInsertionOrder = next;
383    } else {
384      nextInInsertionOrder[prev] = next;
385    }
386    if (next == ENDPOINT) {
387      lastInInsertionOrder = prev;
388    } else {
389      prevInInsertionOrder[next] = prev;
390    }
391  }
392
393  /**
394   * Updates the K-to-V hash table to include the entry at the specified index, which is assumed to
395   * have not yet been added.
396   */
397  private void insertIntoTableKToV(int entry, int keyHash) {
398    checkArgument(entry != ABSENT);
399    int keyBucket = bucket(keyHash);
400    nextInBucketKToV[entry] = hashTableKToV[keyBucket];
401    hashTableKToV[keyBucket] = entry;
402  }
403
404  /**
405   * Updates the V-to-K hash table to include the entry at the specified index, which is assumed to
406   * have not yet been added.
407   */
408  private void insertIntoTableVToK(int entry, int valueHash) {
409    checkArgument(entry != ABSENT);
410    int valueBucket = bucket(valueHash);
411    nextInBucketVToK[entry] = hashTableVToK[valueBucket];
412    hashTableVToK[valueBucket] = entry;
413  }
414
415  /**
416   * Updates the K-to-V hash table to remove the entry at the specified index, which is assumed to
417   * be present. Does not update any other data structures.
418   */
419  private void deleteFromTableKToV(int entry, int keyHash) {
420    checkArgument(entry != ABSENT);
421    int keyBucket = bucket(keyHash);
422
423    if (hashTableKToV[keyBucket] == entry) {
424      hashTableKToV[keyBucket] = nextInBucketKToV[entry];
425      nextInBucketKToV[entry] = ABSENT;
426      return;
427    }
428
429    int prevInBucket = hashTableKToV[keyBucket];
430    for (int entryInBucket = nextInBucketKToV[prevInBucket];
431        entryInBucket != ABSENT;
432        entryInBucket = nextInBucketKToV[entryInBucket]) {
433      if (entryInBucket == entry) {
434        nextInBucketKToV[prevInBucket] = nextInBucketKToV[entry];
435        nextInBucketKToV[entry] = ABSENT;
436        return;
437      }
438      prevInBucket = entryInBucket;
439    }
440    throw new AssertionError("Expected to find entry with key " + keys[entry]);
441  }
442
443  /**
444   * Updates the V-to-K hash table to remove the entry at the specified index, which is assumed to
445   * be present. Does not update any other data structures.
446   */
447  private void deleteFromTableVToK(int entry, int valueHash) {
448    checkArgument(entry != ABSENT);
449    int valueBucket = bucket(valueHash);
450
451    if (hashTableVToK[valueBucket] == entry) {
452      hashTableVToK[valueBucket] = nextInBucketVToK[entry];
453      nextInBucketVToK[entry] = ABSENT;
454      return;
455    }
456
457    int prevInBucket = hashTableVToK[valueBucket];
458    for (int entryInBucket = nextInBucketVToK[prevInBucket];
459        entryInBucket != ABSENT;
460        entryInBucket = nextInBucketVToK[entryInBucket]) {
461      if (entryInBucket == entry) {
462        nextInBucketVToK[prevInBucket] = nextInBucketVToK[entry];
463        nextInBucketVToK[entry] = ABSENT;
464        return;
465      }
466      prevInBucket = entryInBucket;
467    }
468    throw new AssertionError("Expected to find entry with value " + values[entry]);
469  }
470
471  /**
472   * Updates the specified entry to point to the new value: removes the old value from the V-to-K
473   * mapping and puts the new one in. The entry does not move in the insertion order of the bimap.
474   */
475  private void replaceValueInEntry(int entry, @ParametricNullness V newValue, boolean force) {
476    checkArgument(entry != ABSENT);
477    int newValueHash = Hashing.smearedHash(newValue);
478    int newValueIndex = findEntryByValue(newValue, newValueHash);
479    if (newValueIndex != ABSENT) {
480      if (force) {
481        removeEntryValueHashKnown(newValueIndex, newValueHash);
482        if (entry == size) { // this entry got moved to newValueIndex
483          entry = newValueIndex;
484        }
485      } else {
486        throw new IllegalArgumentException("Value already present in map: " + newValue);
487      }
488    }
489    // we do *not* update insertion order, and it isn't a structural modification!
490    deleteFromTableVToK(entry, Hashing.smearedHash(values[entry]));
491    values[entry] = newValue;
492    insertIntoTableVToK(entry, newValueHash);
493  }
494
495  /**
496   * Updates the specified entry to point to the new value: removes the old value from the V-to-K
497   * mapping and puts the new one in. The entry is moved to the end of the insertion order, or to
498   * the position of the new key if it was previously present.
499   */
500  private void replaceKeyInEntry(int entry, @ParametricNullness K newKey, boolean force) {
501    checkArgument(entry != ABSENT);
502    int newKeyHash = Hashing.smearedHash(newKey);
503    int newKeyIndex = findEntryByKey(newKey, newKeyHash);
504
505    int newPredecessor = lastInInsertionOrder;
506    int newSuccessor = ENDPOINT;
507    if (newKeyIndex != ABSENT) {
508      if (force) {
509        newPredecessor = prevInInsertionOrder[newKeyIndex];
510        newSuccessor = nextInInsertionOrder[newKeyIndex];
511        removeEntryKeyHashKnown(newKeyIndex, newKeyHash);
512        if (entry == size) { // this entry got moved to newKeyIndex
513          entry = newKeyIndex;
514        }
515      } else {
516        throw new IllegalArgumentException("Key already present in map: " + newKey);
517      }
518    }
519    if (newPredecessor == entry) {
520      newPredecessor = prevInInsertionOrder[entry];
521    } else if (newPredecessor == size) {
522      newPredecessor = newKeyIndex;
523    }
524
525    if (newSuccessor == entry) {
526      newSuccessor = nextInInsertionOrder[entry];
527    } else if (newSuccessor == size) {
528      newSuccessor = newKeyIndex;
529    }
530
531    int oldPredecessor = prevInInsertionOrder[entry];
532    int oldSuccessor = nextInInsertionOrder[entry];
533    setSucceeds(oldPredecessor, oldSuccessor); // remove from insertion order linked list
534
535    deleteFromTableKToV(entry, Hashing.smearedHash(keys[entry]));
536    keys[entry] = newKey;
537    insertIntoTableKToV(entry, Hashing.smearedHash(newKey));
538
539    // insert into insertion order linked list, usually at the end
540    setSucceeds(newPredecessor, entry);
541    setSucceeds(entry, newSuccessor);
542  }
543
544  @Override
545  @CanIgnoreReturnValue
546  public @Nullable V remove(@Nullable Object key) {
547    int keyHash = Hashing.smearedHash(key);
548    int entry = findEntryByKey(key, keyHash);
549    if (entry == ABSENT) {
550      return null;
551    } else {
552      V value = values[entry];
553      removeEntryKeyHashKnown(entry, keyHash);
554      return value;
555    }
556  }
557
558  @Nullable K removeInverse(@Nullable Object value) {
559    int valueHash = Hashing.smearedHash(value);
560    int entry = findEntryByValue(value, valueHash);
561    if (entry == ABSENT) {
562      return null;
563    } else {
564      K key = keys[entry];
565      removeEntryValueHashKnown(entry, valueHash);
566      return key;
567    }
568  }
569
570  /** Removes the entry at the specified index with no additional data. */
571  void removeEntry(int entry) {
572    removeEntryKeyHashKnown(entry, Hashing.smearedHash(keys[entry]));
573  }
574
575  /** Removes the entry at the specified index, given the hash of its key and value. */
576  private void removeEntry(int entry, int keyHash, int valueHash) {
577    checkArgument(entry != ABSENT);
578    deleteFromTableKToV(entry, keyHash);
579    deleteFromTableVToK(entry, valueHash);
580
581    int oldPredecessor = prevInInsertionOrder[entry];
582    int oldSuccessor = nextInInsertionOrder[entry];
583    setSucceeds(oldPredecessor, oldSuccessor);
584
585    moveEntryToIndex(size - 1, entry);
586    keys[size - 1] = null;
587    values[size - 1] = null;
588    size--;
589    modCount++;
590  }
591
592  /** Removes the entry at the specified index, given the hash of its key. */
593  void removeEntryKeyHashKnown(int entry, int keyHash) {
594    removeEntry(entry, keyHash, Hashing.smearedHash(values[entry]));
595  }
596
597  /** Removes the entry at the specified index, given the hash of its value. */
598  void removeEntryValueHashKnown(int entry, int valueHash) {
599    removeEntry(entry, Hashing.smearedHash(keys[entry]), valueHash);
600  }
601
602  /**
603   * Moves the entry previously positioned at {@code src} to {@code dest}. Assumes the entry
604   * previously at {@code src} has already been removed from the data structures.
605   */
606  private void moveEntryToIndex(int src, int dest) {
607    if (src == dest) {
608      return;
609    }
610    int predecessor = prevInInsertionOrder[src];
611    int successor = nextInInsertionOrder[src];
612    setSucceeds(predecessor, dest);
613    setSucceeds(dest, successor);
614
615    K key = keys[src];
616    V value = values[src];
617
618    keys[dest] = key;
619    values[dest] = value;
620
621    // update pointers in hashTableKToV
622    int keyHash = Hashing.smearedHash(key);
623    int keyBucket = bucket(keyHash);
624    if (hashTableKToV[keyBucket] == src) {
625      hashTableKToV[keyBucket] = dest;
626    } else {
627      int prevInBucket = hashTableKToV[keyBucket];
628      for (int entryInBucket = nextInBucketKToV[prevInBucket];
629          /* should never reach end */ ;
630          entryInBucket = nextInBucketKToV[entryInBucket]) {
631        if (entryInBucket == src) {
632          nextInBucketKToV[prevInBucket] = dest;
633          break;
634        }
635        prevInBucket = entryInBucket;
636      }
637    }
638    nextInBucketKToV[dest] = nextInBucketKToV[src];
639    nextInBucketKToV[src] = ABSENT;
640
641    // update pointers in hashTableVToK
642    int valueHash = Hashing.smearedHash(value);
643    int valueBucket = bucket(valueHash);
644    if (hashTableVToK[valueBucket] == src) {
645      hashTableVToK[valueBucket] = dest;
646    } else {
647      int prevInBucket = hashTableVToK[valueBucket];
648      for (int entryInBucket = nextInBucketVToK[prevInBucket];
649          /* should never reach end*/ ;
650          entryInBucket = nextInBucketVToK[entryInBucket]) {
651        if (entryInBucket == src) {
652          nextInBucketVToK[prevInBucket] = dest;
653          break;
654        }
655        prevInBucket = entryInBucket;
656      }
657    }
658    nextInBucketVToK[dest] = nextInBucketVToK[src];
659    nextInBucketVToK[src] = ABSENT;
660  }
661
662  @Override
663  public void clear() {
664    Arrays.fill(keys, 0, size, null);
665    Arrays.fill(values, 0, size, null);
666    Arrays.fill(hashTableKToV, ABSENT);
667    Arrays.fill(hashTableVToK, ABSENT);
668    Arrays.fill(nextInBucketKToV, 0, size, ABSENT);
669    Arrays.fill(nextInBucketVToK, 0, size, ABSENT);
670    Arrays.fill(prevInInsertionOrder, 0, size, ABSENT);
671    Arrays.fill(nextInInsertionOrder, 0, size, ABSENT);
672    size = 0;
673    firstInInsertionOrder = ENDPOINT;
674    lastInInsertionOrder = ENDPOINT;
675    modCount++;
676  }
677
678  /** Shared supertype of keySet, values, entrySet, and inverse.entrySet. */
679  abstract static class View<
680          K extends @Nullable Object, V extends @Nullable Object, T extends @Nullable Object>
681      extends AbstractSet<T> {
682    final HashBiMap<K, V> biMap;
683
684    View(HashBiMap<K, V> biMap) {
685      this.biMap = biMap;
686    }
687
688    @ParametricNullness
689    abstract T forEntry(int entry);
690
691    @Override
692    public Iterator<T> iterator() {
693      return new Iterator<T>() {
694        private int index = biMap.firstInInsertionOrder;
695        private int indexToRemove = ABSENT;
696        private int expectedModCount = biMap.modCount;
697
698        // Calls to setValue on inverse entries can move already-visited entries to the end.
699        // Make sure we don't visit those.
700        private int remaining = biMap.size;
701
702        private void checkForComodification() {
703          if (biMap.modCount != expectedModCount) {
704            throw new ConcurrentModificationException();
705          }
706        }
707
708        @Override
709        public boolean hasNext() {
710          checkForComodification();
711          return index != ENDPOINT && remaining > 0;
712        }
713
714        @Override
715        @ParametricNullness
716        public T next() {
717          if (!hasNext()) {
718            throw new NoSuchElementException();
719          }
720          T result = forEntry(index);
721          indexToRemove = index;
722          index = biMap.nextInInsertionOrder[index];
723          remaining--;
724          return result;
725        }
726
727        @Override
728        public void remove() {
729          checkForComodification();
730          CollectPreconditions.checkRemove(indexToRemove != ABSENT);
731          biMap.removeEntry(indexToRemove);
732          if (index == biMap.size) {
733            index = indexToRemove;
734          }
735          indexToRemove = ABSENT;
736          expectedModCount = biMap.modCount;
737        }
738      };
739    }
740
741    @Override
742    public int size() {
743      return biMap.size;
744    }
745
746    @Override
747    public void clear() {
748      biMap.clear();
749    }
750  }
751
752  @LazyInit private transient Set<K> keySet;
753
754  @Override
755  public Set<K> keySet() {
756    Set<K> result = keySet;
757    return (result == null) ? keySet = new KeySet() : result;
758  }
759
760  final class KeySet extends View<K, V, K> {
761    KeySet() {
762      super(HashBiMap.this);
763    }
764
765    @Override
766    @ParametricNullness
767    K forEntry(int entry) {
768      // The cast is safe because we call forEntry only for indexes that contain entries.
769      return uncheckedCastNullableTToT(keys[entry]);
770    }
771
772    @Override
773    public boolean contains(@Nullable Object o) {
774      return HashBiMap.this.containsKey(o);
775    }
776
777    @Override
778    public boolean remove(@Nullable Object o) {
779      int oHash = Hashing.smearedHash(o);
780      int entry = findEntryByKey(o, oHash);
781      if (entry != ABSENT) {
782        removeEntryKeyHashKnown(entry, oHash);
783        return true;
784      } else {
785        return false;
786      }
787    }
788  }
789
790  @LazyInit private transient Set<V> valueSet;
791
792  @Override
793  public Set<V> values() {
794    Set<V> result = valueSet;
795    return (result == null) ? valueSet = new ValueSet() : result;
796  }
797
798  final class ValueSet extends View<K, V, V> {
799    ValueSet() {
800      super(HashBiMap.this);
801    }
802
803    @Override
804    @ParametricNullness
805    V forEntry(int entry) {
806      // The cast is safe because we call forEntry only for indexes that contain entries.
807      return uncheckedCastNullableTToT(values[entry]);
808    }
809
810    @Override
811    public boolean contains(@Nullable Object o) {
812      return HashBiMap.this.containsValue(o);
813    }
814
815    @Override
816    public boolean remove(@Nullable Object o) {
817      int oHash = Hashing.smearedHash(o);
818      int entry = findEntryByValue(o, oHash);
819      if (entry != ABSENT) {
820        removeEntryValueHashKnown(entry, oHash);
821        return true;
822      } else {
823        return false;
824      }
825    }
826  }
827
828  @LazyInit private transient Set<Entry<K, V>> entrySet;
829
830  @Override
831  public Set<Entry<K, V>> entrySet() {
832    Set<Entry<K, V>> result = entrySet;
833    return (result == null) ? entrySet = new EntrySet() : result;
834  }
835
836  final class EntrySet extends View<K, V, Entry<K, V>> {
837    EntrySet() {
838      super(HashBiMap.this);
839    }
840
841    @Override
842    public boolean contains(@Nullable Object o) {
843      if (o instanceof Entry) {
844        Entry<?, ?> e = (Entry<?, ?>) o;
845        Object k = e.getKey();
846        Object v = e.getValue();
847        int eIndex = findEntryByKey(k);
848        return eIndex != ABSENT && Objects.equal(v, values[eIndex]);
849      }
850      return false;
851    }
852
853    @Override
854    @CanIgnoreReturnValue
855    public boolean remove(@Nullable Object o) {
856      if (o instanceof Entry) {
857        Entry<?, ?> e = (Entry<?, ?>) o;
858        Object k = e.getKey();
859        Object v = e.getValue();
860        int kHash = Hashing.smearedHash(k);
861        int eIndex = findEntryByKey(k, kHash);
862        if (eIndex != ABSENT && Objects.equal(v, values[eIndex])) {
863          removeEntryKeyHashKnown(eIndex, kHash);
864          return true;
865        }
866      }
867      return false;
868    }
869
870    @Override
871    Entry<K, V> forEntry(int entry) {
872      return new EntryForKey(entry);
873    }
874  }
875
876  /**
877   * An {@code Entry} implementation that attempts to follow its key around the map -- that is, if
878   * the key is moved, deleted, or reinserted, it will account for that -- while not doing any extra
879   * work if the key has not moved. One quirk: The {@link #getValue()} method can return {@code
880   * null} even for a map which supposedly does not contain null elements, if the key is not present
881   * when {@code getValue()} is called.
882   */
883  final class EntryForKey extends AbstractMapEntry<K, V> {
884    @ParametricNullness final K key;
885    int index;
886
887    EntryForKey(int index) {
888      // The cast is safe because we call forEntry only for indexes that contain entries.
889      this.key = uncheckedCastNullableTToT(keys[index]);
890      this.index = index;
891    }
892
893    void updateIndex() {
894      if (index == ABSENT || index > size || !Objects.equal(keys[index], key)) {
895        index = findEntryByKey(key);
896      }
897    }
898
899    @Override
900    @ParametricNullness
901    public K getKey() {
902      return key;
903    }
904
905    @Override
906    @ParametricNullness
907    public V getValue() {
908      updateIndex();
909      /*
910       * If the entry has been removed from the map, we return null, even though that might not be a
911       * valid value. That's the best we can do, short of holding a reference to the most recently
912       * seen value. And while we *could* do that, we aren't required to: Map.Entry explicitly says
913       * that behavior is undefined when the backing map is modified through another API. (It even
914       * permits us to throw IllegalStateException. Maybe we should have done that, but we probably
915       * shouldn't change now for fear of breaking people.)
916       *
917       * If the entry is still in the map, then updateIndex ensured that `index` points to the right
918       * element. Because that element is present, uncheckedCastNullableTToT is safe.
919       */
920      return (index == ABSENT) ? unsafeNull() : uncheckedCastNullableTToT(values[index]);
921    }
922
923    @Override
924    @ParametricNullness
925    public V setValue(@ParametricNullness V value) {
926      updateIndex();
927      if (index == ABSENT) {
928        HashBiMap.this.put(key, value);
929        return unsafeNull(); // See the discussion in getValue().
930      }
931      /*
932       * The cast is safe because updateIndex found the entry for this key. (If it hadn't, then we
933       * would have returned above.) Thus, we know that it and its corresponding value are in
934       * position `index`.
935       */
936      V oldValue = uncheckedCastNullableTToT(values[index]);
937      if (Objects.equal(oldValue, value)) {
938        return value;
939      }
940      replaceValueInEntry(index, value, false);
941      return oldValue;
942    }
943  }
944
945  @LazyInit @RetainedWith private transient @Nullable BiMap<V, K> inverse;
946
947  @Override
948  public BiMap<V, K> inverse() {
949    BiMap<V, K> result = inverse;
950    return (result == null) ? inverse = new Inverse<K, V>(this) : result;
951  }
952
953  static class Inverse<K extends @Nullable Object, V extends @Nullable Object>
954      extends AbstractMap<V, K> implements BiMap<V, K>, Serializable {
955    private final HashBiMap<K, V> forward;
956
957    Inverse(HashBiMap<K, V> forward) {
958      this.forward = forward;
959    }
960
961    @Override
962    public int size() {
963      return forward.size;
964    }
965
966    @Override
967    public boolean containsKey(@Nullable Object key) {
968      return forward.containsValue(key);
969    }
970
971    @Override
972    public @Nullable K get(@Nullable Object key) {
973      return forward.getInverse(key);
974    }
975
976    @Override
977    public boolean containsValue(@Nullable Object value) {
978      return forward.containsKey(value);
979    }
980
981    @Override
982    @CanIgnoreReturnValue
983    public @Nullable K put(@ParametricNullness V value, @ParametricNullness K key) {
984      return forward.putInverse(value, key, false);
985    }
986
987    @Override
988    @CanIgnoreReturnValue
989    public @Nullable K forcePut(@ParametricNullness V value, @ParametricNullness K key) {
990      return forward.putInverse(value, key, true);
991    }
992
993    @Override
994    public BiMap<K, V> inverse() {
995      return forward;
996    }
997
998    @Override
999    @CanIgnoreReturnValue
1000    public @Nullable K remove(@Nullable Object value) {
1001      return forward.removeInverse(value);
1002    }
1003
1004    @Override
1005    public void clear() {
1006      forward.clear();
1007    }
1008
1009    @Override
1010    public Set<V> keySet() {
1011      return forward.values();
1012    }
1013
1014    @Override
1015    public Set<K> values() {
1016      return forward.keySet();
1017    }
1018
1019    private transient Set<Entry<V, K>> inverseEntrySet;
1020
1021    @Override
1022    public Set<Entry<V, K>> entrySet() {
1023      Set<Entry<V, K>> result = inverseEntrySet;
1024      return (result == null) ? inverseEntrySet = new InverseEntrySet<K, V>(forward) : result;
1025    }
1026
1027    @GwtIncompatible("serialization")
1028    private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
1029      in.defaultReadObject();
1030      this.forward.inverse = this;
1031    }
1032  }
1033
1034  static class InverseEntrySet<K extends @Nullable Object, V extends @Nullable Object>
1035      extends View<K, V, Entry<V, K>> {
1036    InverseEntrySet(HashBiMap<K, V> biMap) {
1037      super(biMap);
1038    }
1039
1040    @Override
1041    public boolean contains(@Nullable Object o) {
1042      if (o instanceof Entry) {
1043        Entry<?, ?> e = (Entry<?, ?>) o;
1044        Object v = e.getKey();
1045        Object k = e.getValue();
1046        int eIndex = biMap.findEntryByValue(v);
1047        return eIndex != ABSENT && Objects.equal(biMap.keys[eIndex], k);
1048      }
1049      return false;
1050    }
1051
1052    @Override
1053    public boolean remove(@Nullable Object o) {
1054      if (o instanceof Entry) {
1055        Entry<?, ?> e = (Entry<?, ?>) o;
1056        Object v = e.getKey();
1057        Object k = e.getValue();
1058        int vHash = Hashing.smearedHash(v);
1059        int eIndex = biMap.findEntryByValue(v, vHash);
1060        if (eIndex != ABSENT && Objects.equal(biMap.keys[eIndex], k)) {
1061          biMap.removeEntryValueHashKnown(eIndex, vHash);
1062          return true;
1063        }
1064      }
1065      return false;
1066    }
1067
1068    @Override
1069    Entry<V, K> forEntry(int entry) {
1070      return new EntryForValue<K, V>(biMap, entry);
1071    }
1072  }
1073
1074  /**
1075   * An {@code Entry} implementation that attempts to follow its value around the map -- that is, if
1076   * the value is moved, deleted, or reinserted, it will account for that -- while not doing any
1077   * extra work if the value has not moved.
1078   */
1079  static final class EntryForValue<K extends @Nullable Object, V extends @Nullable Object>
1080      extends AbstractMapEntry<V, K> {
1081    final HashBiMap<K, V> biMap;
1082    @ParametricNullness final V value;
1083    int index;
1084
1085    EntryForValue(HashBiMap<K, V> biMap, int index) {
1086      this.biMap = biMap;
1087      // The cast is safe because we call forEntry only for indexes that contain entries.
1088      this.value = uncheckedCastNullableTToT(biMap.values[index]);
1089      this.index = index;
1090    }
1091
1092    private void updateIndex() {
1093      if (index == ABSENT || index > biMap.size || !Objects.equal(value, biMap.values[index])) {
1094        index = biMap.findEntryByValue(value);
1095      }
1096    }
1097
1098    @Override
1099    @ParametricNullness
1100    public V getKey() {
1101      return value;
1102    }
1103
1104    @Override
1105    @ParametricNullness
1106    public K getValue() {
1107      updateIndex();
1108      // For discussion of unsafeNull() and uncheckedCastNullableTToT(), see EntryForKey.getValue().
1109      return (index == ABSENT) ? unsafeNull() : uncheckedCastNullableTToT(biMap.keys[index]);
1110    }
1111
1112    @Override
1113    @ParametricNullness
1114    public K setValue(@ParametricNullness K key) {
1115      updateIndex();
1116      if (index == ABSENT) {
1117        biMap.putInverse(value, key, false);
1118        return unsafeNull(); // see EntryForKey.setValue()
1119      }
1120      K oldKey = uncheckedCastNullableTToT(biMap.keys[index]); // see EntryForKey.setValue()
1121      if (Objects.equal(oldKey, key)) {
1122        return key;
1123      }
1124      biMap.replaceKeyInEntry(index, key, false);
1125      return oldKey;
1126    }
1127  }
1128
1129  /**
1130   * @serialData the number of entries, first key, first value, second key, second value, and so on.
1131   */
1132  @GwtIncompatible // java.io.ObjectOutputStream
1133  @J2ktIncompatible
1134  private void writeObject(ObjectOutputStream stream) throws IOException {
1135    stream.defaultWriteObject();
1136    Serialization.writeMap(this, stream);
1137  }
1138
1139  @GwtIncompatible // java.io.ObjectInputStream
1140  @J2ktIncompatible
1141  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
1142    stream.defaultReadObject();
1143    int size = Serialization.readCount(stream);
1144    init(16); // resist hostile attempts to allocate gratuitous heap
1145    Serialization.populateMap(this, stream, size);
1146  }
1147}