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 @Override 236 public boolean containsValue(@NullableDecl Object value) { 237 return findEntryByValue(value) != ABSENT; 238 } 239 240 @Override 241 @NullableDecl 242 public V get(@NullableDecl Object key) { 243 int entry = findEntryByKey(key); 244 return (entry == ABSENT) ? null : values[entry]; 245 } 246 247 @NullableDecl 248 K getInverse(@NullableDecl Object value) { 249 int entry = findEntryByValue(value); 250 return (entry == ABSENT) ? null : keys[entry]; 251 } 252 253 @Override 254 @CanIgnoreReturnValue 255 public V put(@NullableDecl K key, @NullableDecl V value) { 256 return put(key, value, false); 257 } 258 259 @NullableDecl 260 V put(@NullableDecl K key, @NullableDecl V value, boolean force) { 261 int keyHash = Hashing.smearedHash(key); 262 int entryForKey = findEntryByKey(key, keyHash); 263 if (entryForKey != ABSENT) { 264 V oldValue = values[entryForKey]; 265 if (Objects.equal(oldValue, value)) { 266 return value; 267 } else { 268 replaceValueInEntry(entryForKey, value, force); 269 return oldValue; 270 } 271 } 272 273 int valueHash = Hashing.smearedHash(value); 274 int valueEntry = findEntryByValue(value, valueHash); 275 if (force) { 276 if (valueEntry != ABSENT) { 277 removeEntryValueHashKnown(valueEntry, valueHash); 278 } 279 } else { 280 checkArgument(valueEntry == ABSENT, "Value already present: %s", value); 281 } 282 283 ensureCapacity(size + 1); 284 keys[size] = key; 285 values[size] = value; 286 287 insertIntoTableKToV(size, keyHash); 288 insertIntoTableVToK(size, valueHash); 289 290 setSucceeds(lastInInsertionOrder, size); 291 setSucceeds(size, ENDPOINT); 292 size++; 293 modCount++; 294 return null; 295 } 296 297 @Override 298 @NullableDecl 299 @CanIgnoreReturnValue 300 public V forcePut(@NullableDecl K key, @NullableDecl V value) { 301 return put(key, value, true); 302 } 303 304 @NullableDecl 305 K putInverse(@NullableDecl V value, @NullableDecl K key, boolean force) { 306 int valueHash = Hashing.smearedHash(value); 307 int entryForValue = findEntryByValue(value, valueHash); 308 if (entryForValue != ABSENT) { 309 K oldKey = keys[entryForValue]; 310 if (Objects.equal(oldKey, key)) { 311 return key; 312 } else { 313 replaceKeyInEntry(entryForValue, key, force); 314 return oldKey; 315 } 316 } 317 318 int predecessor = lastInInsertionOrder; 319 int keyHash = Hashing.smearedHash(key); 320 int keyEntry = findEntryByKey(key, keyHash); 321 if (force) { 322 if (keyEntry != ABSENT) { 323 predecessor = prevInInsertionOrder[keyEntry]; 324 removeEntryKeyHashKnown(keyEntry, keyHash); 325 } 326 } else { 327 checkArgument(keyEntry == ABSENT, "Key already present: %s", key); 328 } 329 330 // insertion point for new entry is after predecessor 331 // note predecessor must still be a valid entry: either we deleted an entry that was *not* 332 // predecessor, or we didn't delete anything 333 334 ensureCapacity(size + 1); 335 keys[size] = key; 336 values[size] = value; 337 338 insertIntoTableKToV(size, keyHash); 339 insertIntoTableVToK(size, valueHash); 340 341 int successor = 342 (predecessor == ENDPOINT) ? firstInInsertionOrder : nextInInsertionOrder[predecessor]; 343 setSucceeds(predecessor, size); 344 setSucceeds(size, successor); 345 size++; 346 modCount++; 347 return null; 348 } 349 350 /** 351 * Updates the pointers of the insertion order linked list so that {@code next} follows {@code 352 * prev}. {@code ENDPOINT} represents either the first or last entry in the entire map (as 353 * appropriate). 354 */ 355 private void setSucceeds(int prev, int next) { 356 if (prev == ENDPOINT) { 357 firstInInsertionOrder = next; 358 } else { 359 nextInInsertionOrder[prev] = next; 360 } 361 if (next == ENDPOINT) { 362 lastInInsertionOrder = prev; 363 } else { 364 prevInInsertionOrder[next] = prev; 365 } 366 } 367 368 /** 369 * Updates the K-to-V hash table to include the entry at the specified index, which is assumed to 370 * have not yet been added. 371 */ 372 private void insertIntoTableKToV(int entry, int keyHash) { 373 checkArgument(entry != ABSENT); 374 int keyBucket = bucket(keyHash); 375 nextInBucketKToV[entry] = hashTableKToV[keyBucket]; 376 hashTableKToV[keyBucket] = entry; 377 } 378 379 /** 380 * Updates the V-to-K hash table to include the entry at the specified index, which is assumed to 381 * have not yet been added. 382 */ 383 private void insertIntoTableVToK(int entry, int valueHash) { 384 checkArgument(entry != ABSENT); 385 int valueBucket = bucket(valueHash); 386 nextInBucketVToK[entry] = hashTableVToK[valueBucket]; 387 hashTableVToK[valueBucket] = entry; 388 } 389 390 /** 391 * Updates the K-to-V hash table to remove the entry at the specified index, which is assumed to 392 * be present. Does not update any other data structures. 393 */ 394 private void deleteFromTableKToV(int entry, int keyHash) { 395 checkArgument(entry != ABSENT); 396 int keyBucket = bucket(keyHash); 397 398 if (hashTableKToV[keyBucket] == entry) { 399 hashTableKToV[keyBucket] = nextInBucketKToV[entry]; 400 nextInBucketKToV[entry] = ABSENT; 401 return; 402 } 403 404 int prevInBucket = hashTableKToV[keyBucket]; 405 for (int entryInBucket = nextInBucketKToV[prevInBucket]; 406 entryInBucket != ABSENT; 407 entryInBucket = nextInBucketKToV[entryInBucket]) { 408 if (entryInBucket == entry) { 409 nextInBucketKToV[prevInBucket] = nextInBucketKToV[entry]; 410 nextInBucketKToV[entry] = ABSENT; 411 return; 412 } 413 prevInBucket = entryInBucket; 414 } 415 throw new AssertionError("Expected to find entry with key " + keys[entry]); 416 } 417 418 /** 419 * Updates the V-to-K hash table to remove the entry at the specified index, which is assumed to 420 * be present. Does not update any other data structures. 421 */ 422 private void deleteFromTableVToK(int entry, int valueHash) { 423 checkArgument(entry != ABSENT); 424 int valueBucket = bucket(valueHash); 425 426 if (hashTableVToK[valueBucket] == entry) { 427 hashTableVToK[valueBucket] = nextInBucketVToK[entry]; 428 nextInBucketVToK[entry] = ABSENT; 429 return; 430 } 431 432 int prevInBucket = hashTableVToK[valueBucket]; 433 for (int entryInBucket = nextInBucketVToK[prevInBucket]; 434 entryInBucket != ABSENT; 435 entryInBucket = nextInBucketVToK[entryInBucket]) { 436 if (entryInBucket == entry) { 437 nextInBucketVToK[prevInBucket] = nextInBucketVToK[entry]; 438 nextInBucketVToK[entry] = ABSENT; 439 return; 440 } 441 prevInBucket = entryInBucket; 442 } 443 throw new AssertionError("Expected to find entry with value " + values[entry]); 444 } 445 446 /** 447 * Updates the specified entry to point to the new value: removes the old value from the V-to-K 448 * mapping and puts the new one in. The entry does not move in the insertion order of the bimap. 449 */ 450 private void replaceValueInEntry(int entry, @NullableDecl V newValue, boolean force) { 451 checkArgument(entry != ABSENT); 452 int newValueHash = Hashing.smearedHash(newValue); 453 int newValueIndex = findEntryByValue(newValue, newValueHash); 454 if (newValueIndex != ABSENT) { 455 if (force) { 456 removeEntryValueHashKnown(newValueIndex, newValueHash); 457 if (entry == size) { // this entry got moved to newValueIndex 458 entry = newValueIndex; 459 } 460 } else { 461 throw new IllegalArgumentException("Value already present in map: " + newValue); 462 } 463 } 464 // we do *not* update insertion order, and it isn't a structural modification! 465 deleteFromTableVToK(entry, Hashing.smearedHash(values[entry])); 466 values[entry] = newValue; 467 insertIntoTableVToK(entry, newValueHash); 468 } 469 470 /** 471 * Updates the specified entry to point to the new value: removes the old value from the V-to-K 472 * mapping and puts the new one in. The entry is moved to the end of the insertion order, or to 473 * the position of the new key if it was previously present. 474 */ 475 private void replaceKeyInEntry(int entry, @NullableDecl K newKey, boolean force) { 476 checkArgument(entry != ABSENT); 477 int newKeyHash = Hashing.smearedHash(newKey); 478 int newKeyIndex = findEntryByKey(newKey, newKeyHash); 479 480 int newPredecessor = lastInInsertionOrder; 481 int newSuccessor = ENDPOINT; 482 if (newKeyIndex != ABSENT) { 483 if (force) { 484 newPredecessor = prevInInsertionOrder[newKeyIndex]; 485 newSuccessor = nextInInsertionOrder[newKeyIndex]; 486 removeEntryKeyHashKnown(newKeyIndex, newKeyHash); 487 if (entry == size) { // this entry got moved to newKeyIndex 488 entry = newKeyIndex; 489 } 490 } else { 491 throw new IllegalArgumentException("Key already present in map: " + newKey); 492 } 493 } 494 if (newPredecessor == entry) { 495 newPredecessor = prevInInsertionOrder[entry]; 496 } else if (newPredecessor == size) { 497 newPredecessor = newKeyIndex; 498 } 499 500 if (newSuccessor == entry) { 501 newSuccessor = nextInInsertionOrder[entry]; 502 } else if (newSuccessor == size) { 503 newSuccessor = newKeyIndex; 504 } 505 506 int oldPredecessor = prevInInsertionOrder[entry]; 507 int oldSuccessor = nextInInsertionOrder[entry]; 508 setSucceeds(oldPredecessor, oldSuccessor); // remove from insertion order linked list 509 510 deleteFromTableKToV(entry, Hashing.smearedHash(keys[entry])); 511 keys[entry] = newKey; 512 insertIntoTableKToV(entry, Hashing.smearedHash(newKey)); 513 514 // insert into insertion order linked list, usually at the end 515 setSucceeds(newPredecessor, entry); 516 setSucceeds(entry, newSuccessor); 517 } 518 519 @Override 520 @NullableDecl 521 @CanIgnoreReturnValue 522 public V remove(@NullableDecl Object key) { 523 int keyHash = Hashing.smearedHash(key); 524 int entry = findEntryByKey(key, keyHash); 525 if (entry == ABSENT) { 526 return null; 527 } else { 528 @NullableDecl V value = values[entry]; 529 removeEntryKeyHashKnown(entry, keyHash); 530 return value; 531 } 532 } 533 534 @NullableDecl 535 K removeInverse(@NullableDecl Object value) { 536 int valueHash = Hashing.smearedHash(value); 537 int entry = findEntryByValue(value, valueHash); 538 if (entry == ABSENT) { 539 return null; 540 } else { 541 @NullableDecl K key = keys[entry]; 542 removeEntryValueHashKnown(entry, valueHash); 543 return key; 544 } 545 } 546 547 /** Removes the entry at the specified index with no additional data. */ 548 void removeEntry(int entry) { 549 removeEntryKeyHashKnown(entry, Hashing.smearedHash(keys[entry])); 550 } 551 552 /** Removes the entry at the specified index, given the hash of its key and value. */ 553 private void removeEntry(int entry, int keyHash, int valueHash) { 554 checkArgument(entry != ABSENT); 555 deleteFromTableKToV(entry, keyHash); 556 deleteFromTableVToK(entry, valueHash); 557 558 int oldPredecessor = prevInInsertionOrder[entry]; 559 int oldSuccessor = nextInInsertionOrder[entry]; 560 setSucceeds(oldPredecessor, oldSuccessor); 561 562 moveEntryToIndex(size - 1, entry); 563 keys[size - 1] = null; 564 values[size - 1] = null; 565 size--; 566 modCount++; 567 } 568 569 /** Removes the entry at the specified index, given the hash of its key. */ 570 void removeEntryKeyHashKnown(int entry, int keyHash) { 571 removeEntry(entry, keyHash, Hashing.smearedHash(values[entry])); 572 } 573 574 /** Removes the entry at the specified index, given the hash of its value. */ 575 void removeEntryValueHashKnown(int entry, int valueHash) { 576 removeEntry(entry, Hashing.smearedHash(keys[entry]), valueHash); 577 } 578 579 /** 580 * Moves the entry previously positioned at {@code src} to {@code dest}. Assumes the entry 581 * previously at {@code src} has already been removed from the data structures. 582 */ 583 private void moveEntryToIndex(int src, int dest) { 584 if (src == dest) { 585 return; 586 } 587 int predecessor = prevInInsertionOrder[src]; 588 int successor = nextInInsertionOrder[src]; 589 setSucceeds(predecessor, dest); 590 setSucceeds(dest, successor); 591 592 K key = keys[src]; 593 V value = values[src]; 594 595 keys[dest] = key; 596 values[dest] = value; 597 598 // update pointers in hashTableKToV 599 int keyHash = Hashing.smearedHash(key); 600 int keyBucket = bucket(keyHash); 601 if (hashTableKToV[keyBucket] == src) { 602 hashTableKToV[keyBucket] = dest; 603 } else { 604 int prevInBucket = hashTableKToV[keyBucket]; 605 for (int entryInBucket = nextInBucketKToV[prevInBucket]; 606 /* should never reach end */ ; 607 entryInBucket = nextInBucketKToV[entryInBucket]) { 608 if (entryInBucket == src) { 609 nextInBucketKToV[prevInBucket] = dest; 610 break; 611 } 612 prevInBucket = entryInBucket; 613 } 614 } 615 nextInBucketKToV[dest] = nextInBucketKToV[src]; 616 nextInBucketKToV[src] = ABSENT; 617 618 // update pointers in hashTableVToK 619 int valueHash = Hashing.smearedHash(value); 620 int valueBucket = bucket(valueHash); 621 if (hashTableVToK[valueBucket] == src) { 622 hashTableVToK[valueBucket] = dest; 623 } else { 624 int prevInBucket = hashTableVToK[valueBucket]; 625 for (int entryInBucket = nextInBucketVToK[prevInBucket]; 626 /* should never reach end*/ ; 627 entryInBucket = nextInBucketVToK[entryInBucket]) { 628 if (entryInBucket == src) { 629 nextInBucketVToK[prevInBucket] = dest; 630 break; 631 } 632 prevInBucket = entryInBucket; 633 } 634 } 635 nextInBucketVToK[dest] = nextInBucketVToK[src]; 636 nextInBucketVToK[src] = ABSENT; 637 } 638 639 @Override 640 public void clear() { 641 Arrays.fill(keys, 0, size, null); 642 Arrays.fill(values, 0, size, null); 643 Arrays.fill(hashTableKToV, ABSENT); 644 Arrays.fill(hashTableVToK, ABSENT); 645 Arrays.fill(nextInBucketKToV, 0, size, ABSENT); 646 Arrays.fill(nextInBucketVToK, 0, size, ABSENT); 647 Arrays.fill(prevInInsertionOrder, 0, size, ABSENT); 648 Arrays.fill(nextInInsertionOrder, 0, size, ABSENT); 649 size = 0; 650 firstInInsertionOrder = ENDPOINT; 651 lastInInsertionOrder = ENDPOINT; 652 modCount++; 653 } 654 655 /** Shared supertype of keySet, values, entrySet, and inverse.entrySet. */ 656 abstract static class View<K, V, T> extends AbstractSet<T> { 657 final HashBiMap<K, V> biMap; 658 659 View(HashBiMap<K, V> biMap) { 660 this.biMap = biMap; 661 } 662 663 abstract T forEntry(int entry); 664 665 @Override 666 public Iterator<T> iterator() { 667 return new Iterator<T>() { 668 private int index = biMap.firstInInsertionOrder; 669 private int indexToRemove = ABSENT; 670 private int expectedModCount = biMap.modCount; 671 672 // Calls to setValue on inverse entries can move already-visited entries to the end. 673 // Make sure we don't visit those. 674 private int remaining = biMap.size; 675 676 private void checkForComodification() { 677 if (biMap.modCount != expectedModCount) { 678 throw new ConcurrentModificationException(); 679 } 680 } 681 682 @Override 683 public boolean hasNext() { 684 checkForComodification(); 685 return index != ENDPOINT && remaining > 0; 686 } 687 688 @Override 689 public T next() { 690 if (!hasNext()) { 691 throw new NoSuchElementException(); 692 } 693 T result = forEntry(index); 694 indexToRemove = index; 695 index = biMap.nextInInsertionOrder[index]; 696 remaining--; 697 return result; 698 } 699 700 @Override 701 public void remove() { 702 checkForComodification(); 703 CollectPreconditions.checkRemove(indexToRemove != ABSENT); 704 biMap.removeEntry(indexToRemove); 705 if (index == biMap.size) { 706 index = indexToRemove; 707 } 708 indexToRemove = ABSENT; 709 expectedModCount = biMap.modCount; 710 } 711 }; 712 } 713 714 @Override 715 public int size() { 716 return biMap.size; 717 } 718 719 @Override 720 public void clear() { 721 biMap.clear(); 722 } 723 } 724 725 private transient Set<K> keySet; 726 727 @Override 728 public Set<K> keySet() { 729 Set<K> result = keySet; 730 return (result == null) ? keySet = new KeySet() : result; 731 } 732 733 final class KeySet extends View<K, V, K> { 734 KeySet() { 735 super(HashBiMap.this); 736 } 737 738 @Override 739 K forEntry(int entry) { 740 return keys[entry]; 741 } 742 743 @Override 744 public boolean contains(@NullableDecl Object o) { 745 return HashBiMap.this.containsKey(o); 746 } 747 748 @Override 749 public boolean remove(@NullableDecl Object o) { 750 int oHash = Hashing.smearedHash(o); 751 int entry = findEntryByKey(o, oHash); 752 if (entry != ABSENT) { 753 removeEntryKeyHashKnown(entry, oHash); 754 return true; 755 } else { 756 return false; 757 } 758 } 759 } 760 761 private transient Set<V> valueSet; 762 763 @Override 764 public Set<V> values() { 765 Set<V> result = valueSet; 766 return (result == null) ? valueSet = new ValueSet() : result; 767 } 768 769 final class ValueSet extends View<K, V, V> { 770 ValueSet() { 771 super(HashBiMap.this); 772 } 773 774 @Override 775 V forEntry(int entry) { 776 return values[entry]; 777 } 778 779 @Override 780 public boolean contains(@NullableDecl Object o) { 781 return HashBiMap.this.containsValue(o); 782 } 783 784 @Override 785 public boolean remove(@NullableDecl Object o) { 786 int oHash = Hashing.smearedHash(o); 787 int entry = findEntryByValue(o, oHash); 788 if (entry != ABSENT) { 789 removeEntryValueHashKnown(entry, oHash); 790 return true; 791 } else { 792 return false; 793 } 794 } 795 } 796 797 private transient Set<Entry<K, V>> entrySet; 798 799 @Override 800 public Set<Entry<K, V>> entrySet() { 801 Set<Entry<K, V>> result = entrySet; 802 return (result == null) ? entrySet = new EntrySet() : result; 803 } 804 805 final class EntrySet extends View<K, V, Entry<K, V>> { 806 EntrySet() { 807 super(HashBiMap.this); 808 } 809 810 @Override 811 public boolean contains(@NullableDecl Object o) { 812 if (o instanceof Entry) { 813 Entry<?, ?> e = (Entry<?, ?>) o; 814 @NullableDecl Object k = e.getKey(); 815 @NullableDecl Object v = e.getValue(); 816 int eIndex = findEntryByKey(k); 817 return eIndex != ABSENT && Objects.equal(v, values[eIndex]); 818 } 819 return false; 820 } 821 822 @Override 823 @CanIgnoreReturnValue 824 public boolean remove(@NullableDecl Object o) { 825 if (o instanceof Entry) { 826 Entry<?, ?> e = (Entry<?, ?>) o; 827 @NullableDecl Object k = e.getKey(); 828 @NullableDecl Object v = e.getValue(); 829 int kHash = Hashing.smearedHash(k); 830 int eIndex = findEntryByKey(k, kHash); 831 if (eIndex != ABSENT && Objects.equal(v, values[eIndex])) { 832 removeEntryKeyHashKnown(eIndex, kHash); 833 return true; 834 } 835 } 836 return false; 837 } 838 839 @Override 840 Entry<K, V> forEntry(int entry) { 841 return new EntryForKey(entry); 842 } 843 } 844 845 /** 846 * An {@code Entry} implementation that attempts to follow its key around the map -- that is, if 847 * the key is moved, deleted, or reinserted, it will account for that -- while not doing any extra 848 * work if the key has not moved. 849 */ 850 final class EntryForKey extends AbstractMapEntry<K, V> { 851 @NullableDecl final K key; 852 int index; 853 854 EntryForKey(int index) { 855 this.key = keys[index]; 856 this.index = index; 857 } 858 859 void updateIndex() { 860 if (index == ABSENT || index > size || !Objects.equal(keys[index], key)) { 861 index = findEntryByKey(key); 862 } 863 } 864 865 @Override 866 public K getKey() { 867 return key; 868 } 869 870 @Override 871 @NullableDecl 872 public V getValue() { 873 updateIndex(); 874 return (index == ABSENT) ? null : values[index]; 875 } 876 877 @Override 878 public V setValue(V value) { 879 updateIndex(); 880 if (index == ABSENT) { 881 return HashBiMap.this.put(key, value); 882 } 883 V oldValue = values[index]; 884 if (Objects.equal(oldValue, value)) { 885 return value; 886 } 887 replaceValueInEntry(index, value, false); 888 return oldValue; 889 } 890 } 891 892 @MonotonicNonNullDecl @RetainedWith private transient BiMap<V, K> inverse; 893 894 @Override 895 public BiMap<V, K> inverse() { 896 BiMap<V, K> result = inverse; 897 return (result == null) ? inverse = new Inverse<K, V>(this) : result; 898 } 899 900 static class Inverse<K, V> extends AbstractMap<V, K> implements BiMap<V, K>, Serializable { 901 private final HashBiMap<K, V> forward; 902 903 Inverse(HashBiMap<K, V> forward) { 904 this.forward = forward; 905 } 906 907 @Override 908 public int size() { 909 return forward.size; 910 } 911 912 @Override 913 public boolean containsKey(@NullableDecl Object key) { 914 return forward.containsValue(key); 915 } 916 917 @Override 918 @NullableDecl 919 public K get(@NullableDecl Object key) { 920 return forward.getInverse(key); 921 } 922 923 @Override 924 public boolean containsValue(@NullableDecl Object value) { 925 return forward.containsKey(value); 926 } 927 928 @Override 929 @NullableDecl 930 @CanIgnoreReturnValue 931 public K put(@NullableDecl V value, @NullableDecl K key) { 932 return forward.putInverse(value, key, false); 933 } 934 935 @Override 936 @NullableDecl 937 @CanIgnoreReturnValue 938 public K forcePut(@NullableDecl V value, @NullableDecl K key) { 939 return forward.putInverse(value, key, true); 940 } 941 942 @Override 943 public BiMap<K, V> inverse() { 944 return forward; 945 } 946 947 @Override 948 @NullableDecl 949 @CanIgnoreReturnValue 950 public K remove(@NullableDecl Object value) { 951 return forward.removeInverse(value); 952 } 953 954 @Override 955 public void clear() { 956 forward.clear(); 957 } 958 959 @Override 960 public Set<V> keySet() { 961 return forward.values(); 962 } 963 964 @Override 965 public Set<K> values() { 966 return forward.keySet(); 967 } 968 969 private transient Set<Entry<V, K>> inverseEntrySet; 970 971 @Override 972 public Set<Entry<V, K>> entrySet() { 973 Set<Entry<V, K>> result = inverseEntrySet; 974 return (result == null) ? inverseEntrySet = new InverseEntrySet<K, V>(forward) : result; 975 } 976 977 @GwtIncompatible("serialization") 978 private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { 979 in.defaultReadObject(); 980 this.forward.inverse = this; 981 } 982 } 983 984 static class InverseEntrySet<K, V> extends View<K, V, Entry<V, K>> { 985 InverseEntrySet(HashBiMap<K, V> biMap) { 986 super(biMap); 987 } 988 989 @Override 990 public boolean contains(@NullableDecl Object o) { 991 if (o instanceof Entry) { 992 Entry<?, ?> e = (Entry<?, ?>) o; 993 Object v = e.getKey(); 994 Object k = e.getValue(); 995 int eIndex = biMap.findEntryByValue(v); 996 return eIndex != ABSENT && Objects.equal(biMap.keys[eIndex], k); 997 } 998 return false; 999 } 1000 1001 @Override 1002 public boolean remove(Object o) { 1003 if (o instanceof Entry) { 1004 Entry<?, ?> e = (Entry<?, ?>) o; 1005 Object v = e.getKey(); 1006 Object k = e.getValue(); 1007 int vHash = Hashing.smearedHash(v); 1008 int eIndex = biMap.findEntryByValue(v, vHash); 1009 if (eIndex != ABSENT && Objects.equal(biMap.keys[eIndex], k)) { 1010 biMap.removeEntryValueHashKnown(eIndex, vHash); 1011 return true; 1012 } 1013 } 1014 return false; 1015 } 1016 1017 @Override 1018 Entry<V, K> forEntry(int entry) { 1019 return new EntryForValue<K, V>(biMap, entry); 1020 } 1021 } 1022 1023 /** 1024 * An {@code Entry} implementation that attempts to follow its value around the map -- that is, if 1025 * the value is moved, deleted, or reinserted, it will account for that -- while not doing any 1026 * extra work if the value has not moved. 1027 */ 1028 static final class EntryForValue<K, V> extends AbstractMapEntry<V, K> { 1029 final HashBiMap<K, V> biMap; 1030 final V value; 1031 int index; 1032 1033 EntryForValue(HashBiMap<K, V> biMap, int index) { 1034 this.biMap = biMap; 1035 this.value = biMap.values[index]; 1036 this.index = index; 1037 } 1038 1039 private void updateIndex() { 1040 if (index == ABSENT || index > biMap.size || !Objects.equal(value, biMap.values[index])) { 1041 index = biMap.findEntryByValue(value); 1042 } 1043 } 1044 1045 @Override 1046 public V getKey() { 1047 return value; 1048 } 1049 1050 @Override 1051 public K getValue() { 1052 updateIndex(); 1053 return (index == ABSENT) ? null : biMap.keys[index]; 1054 } 1055 1056 @Override 1057 public K setValue(K key) { 1058 updateIndex(); 1059 if (index == ABSENT) { 1060 return biMap.putInverse(value, key, false); 1061 } 1062 K oldKey = biMap.keys[index]; 1063 if (Objects.equal(oldKey, key)) { 1064 return key; 1065 } 1066 biMap.replaceKeyInEntry(index, key, false); 1067 return oldKey; 1068 } 1069 } 1070 1071 /** 1072 * @serialData the number of entries, first key, first value, second key, second value, and so on. 1073 */ 1074 @GwtIncompatible // java.io.ObjectOutputStream 1075 private void writeObject(ObjectOutputStream stream) throws IOException { 1076 stream.defaultWriteObject(); 1077 Serialization.writeMap(this, stream); 1078 } 1079 1080 @GwtIncompatible // java.io.ObjectInputStream 1081 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 1082 stream.defaultReadObject(); 1083 int size = Serialization.readCount(stream); 1084 init(16); // resist hostile attempts to allocate gratuitous heap 1085 Serialization.populateMap(this, stream, size); 1086 } 1087}