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