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}