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