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