Coverage Summary for Class: RegularImmutableBiMap (com.google.common.collect)

Class Method, % Line, %
RegularImmutableBiMap 13.3% (2/15) 12.5% (8/64)
RegularImmutableBiMap$Inverse 0% (0/9) 0% (0/19)
RegularImmutableBiMap$Inverse$InverseEntrySet 0% (0/7) 0% (0/7)
RegularImmutableBiMap$Inverse$InverseEntrySet$1 0% (0/3) 0% (0/4)
RegularImmutableBiMap$InverseSerializedForm 0% (0/2) 0% (0/3)
Total 5.6% (2/36) 8.2% (8/97)


1 /* 2  * Copyright (C) 2008 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); 5  * you may not use this file except in compliance with the License. 6  * You may obtain a copy of the License at 7  * 8  * http://www.apache.org/licenses/LICENSE-2.0 9  * 10  * Unless required by applicable law or agreed to in writing, software 11  * distributed under the License is distributed on an "AS IS" BASIS, 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13  * See the License for the specific language governing permissions and 14  * limitations under the License. 15  */ 16  17 package com.google.common.collect; 18  19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static com.google.common.base.Preconditions.checkPositionIndex; 21 import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; 22 import static com.google.common.collect.ImmutableMapEntry.createEntryArray; 23 import static com.google.common.collect.RegularImmutableMap.checkNoConflictInKeyBucket; 24  25 import com.google.common.annotations.GwtCompatible; 26 import com.google.common.annotations.VisibleForTesting; 27 import com.google.common.collect.ImmutableMapEntry.NonTerminalImmutableBiMapEntry; 28 import com.google.errorprone.annotations.CanIgnoreReturnValue; 29 import com.google.errorprone.annotations.concurrent.LazyInit; 30 import com.google.j2objc.annotations.RetainedWith; 31 import java.io.Serializable; 32 import java.util.function.BiConsumer; 33 import java.util.function.Consumer; 34 import org.checkerframework.checker.nullness.qual.Nullable; 35  36 /** 37  * Bimap with zero or more mappings. 38  * 39  * @author Louis Wasserman 40  */ 41 @GwtCompatible(serializable = true, emulated = true) 42 @SuppressWarnings("serial") // uses writeReplace(), not default serialization 43 class RegularImmutableBiMap<K, V> extends ImmutableBiMap<K, V> { 44  static final RegularImmutableBiMap<Object, Object> EMPTY = 45  new RegularImmutableBiMap<>( 46  null, null, (Entry<Object, Object>[]) ImmutableMap.EMPTY_ENTRY_ARRAY, 0, 0); 47  48  static final double MAX_LOAD_FACTOR = 1.2; 49  50  private final transient ImmutableMapEntry<K, V>[] keyTable; 51  private final transient ImmutableMapEntry<K, V>[] valueTable; 52  @VisibleForTesting final transient Entry<K, V>[] entries; 53  private final transient int mask; 54  private final transient int hashCode; 55  56  static <K, V> ImmutableBiMap<K, V> fromEntries(Entry<K, V>... entries) { 57  return fromEntryArray(entries.length, entries); 58  } 59  60  static <K, V> ImmutableBiMap<K, V> fromEntryArray(int n, Entry<K, V>[] entryArray) { 61  checkPositionIndex(n, entryArray.length); 62  int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); 63  int mask = tableSize - 1; 64  ImmutableMapEntry<K, V>[] keyTable = createEntryArray(tableSize); 65  ImmutableMapEntry<K, V>[] valueTable = createEntryArray(tableSize); 66  Entry<K, V>[] entries; 67  if (n == entryArray.length) { 68  entries = entryArray; 69  } else { 70  entries = createEntryArray(n); 71  } 72  int hashCode = 0; 73  74  for (int i = 0; i < n; i++) { 75  @SuppressWarnings("unchecked") 76  Entry<K, V> entry = entryArray[i]; 77  K key = entry.getKey(); 78  V value = entry.getValue(); 79  checkEntryNotNull(key, value); 80  int keyHash = key.hashCode(); 81  int valueHash = value.hashCode(); 82  int keyBucket = Hashing.smear(keyHash) & mask; 83  int valueBucket = Hashing.smear(valueHash) & mask; 84  85  ImmutableMapEntry<K, V> nextInKeyBucket = keyTable[keyBucket]; 86  int keyBucketLength = checkNoConflictInKeyBucket(key, entry, nextInKeyBucket); 87  ImmutableMapEntry<K, V> nextInValueBucket = valueTable[valueBucket]; 88  int valueBucketLength = checkNoConflictInValueBucket(value, entry, nextInValueBucket); 89  if (keyBucketLength > RegularImmutableMap.MAX_HASH_BUCKET_LENGTH 90  || valueBucketLength > RegularImmutableMap.MAX_HASH_BUCKET_LENGTH) { 91  return JdkBackedImmutableBiMap.create(n, entryArray); 92  } 93  ImmutableMapEntry<K, V> newEntry = 94  (nextInValueBucket == null && nextInKeyBucket == null) 95  ? RegularImmutableMap.makeImmutable(entry, key, value) 96  : new NonTerminalImmutableBiMapEntry<>( 97  key, value, nextInKeyBucket, nextInValueBucket); 98  keyTable[keyBucket] = newEntry; 99  valueTable[valueBucket] = newEntry; 100  entries[i] = newEntry; 101  hashCode += keyHash ^ valueHash; 102  } 103  return new RegularImmutableBiMap<>(keyTable, valueTable, entries, mask, hashCode); 104  } 105  106  private RegularImmutableBiMap( 107  ImmutableMapEntry<K, V>[] keyTable, 108  ImmutableMapEntry<K, V>[] valueTable, 109  Entry<K, V>[] entries, 110  int mask, 111  int hashCode) { 112  this.keyTable = keyTable; 113  this.valueTable = valueTable; 114  this.entries = entries; 115  this.mask = mask; 116  this.hashCode = hashCode; 117  } 118  119  // checkNoConflictInKeyBucket is static imported from RegularImmutableMap 120  121  /** 122  * @return number of entries in this bucket 123  * @throws IllegalArgumentException if another entry in the bucket has the same key 124  */ 125  @CanIgnoreReturnValue 126  private static int checkNoConflictInValueBucket( 127  Object value, Entry<?, ?> entry, @Nullable ImmutableMapEntry<?, ?> valueBucketHead) { 128  int bucketSize = 0; 129  for (; valueBucketHead != null; valueBucketHead = valueBucketHead.getNextInValueBucket()) { 130  checkNoConflict(!value.equals(valueBucketHead.getValue()), "value", entry, valueBucketHead); 131  bucketSize++; 132  } 133  return bucketSize; 134  } 135  136  @Override 137  public @Nullable V get(@Nullable Object key) { 138  return (keyTable == null) ? null : RegularImmutableMap.get(key, keyTable, mask); 139  } 140  141  @Override 142  ImmutableSet<Entry<K, V>> createEntrySet() { 143  return isEmpty() 144  ? ImmutableSet.<Entry<K, V>>of() 145  : new ImmutableMapEntrySet.RegularEntrySet<K, V>(this, entries); 146  } 147  148  @Override 149  ImmutableSet<K> createKeySet() { 150  return new ImmutableMapKeySet<>(this); 151  } 152  153  @Override 154  public void forEach(BiConsumer<? super K, ? super V> action) { 155  checkNotNull(action); 156  for (Entry<K, V> entry : entries) { 157  action.accept(entry.getKey(), entry.getValue()); 158  } 159  } 160  161  @Override 162  boolean isHashCodeFast() { 163  return true; 164  } 165  166  @Override 167  public int hashCode() { 168  return hashCode; 169  } 170  171  @Override 172  boolean isPartialView() { 173  return false; 174  } 175  176  @Override 177  public int size() { 178  return entries.length; 179  } 180  181  @LazyInit @RetainedWith private transient ImmutableBiMap<V, K> inverse; 182  183  @Override 184  public ImmutableBiMap<V, K> inverse() { 185  if (isEmpty()) { 186  return ImmutableBiMap.of(); 187  } 188  ImmutableBiMap<V, K> result = inverse; 189  return (result == null) ? inverse = new Inverse() : result; 190  } 191  192  private final class Inverse extends ImmutableBiMap<V, K> { 193  194  @Override 195  public int size() { 196  return inverse().size(); 197  } 198  199  @Override 200  public ImmutableBiMap<K, V> inverse() { 201  return RegularImmutableBiMap.this; 202  } 203  204  @Override 205  public void forEach(BiConsumer<? super V, ? super K> action) { 206  checkNotNull(action); 207  RegularImmutableBiMap.this.forEach((k, v) -> action.accept(v, k)); 208  } 209  210  @Override 211  public K get(@Nullable Object value) { 212  if (value == null || valueTable == null) { 213  return null; 214  } 215  int bucket = Hashing.smear(value.hashCode()) & mask; 216  for (ImmutableMapEntry<K, V> entry = valueTable[bucket]; 217  entry != null; 218  entry = entry.getNextInValueBucket()) { 219  if (value.equals(entry.getValue())) { 220  return entry.getKey(); 221  } 222  } 223  return null; 224  } 225  226  @Override 227  ImmutableSet<V> createKeySet() { 228  return new ImmutableMapKeySet<>(this); 229  } 230  231  @Override 232  ImmutableSet<Entry<V, K>> createEntrySet() { 233  return new InverseEntrySet(); 234  } 235  236  final class InverseEntrySet extends ImmutableMapEntrySet<V, K> { 237  @Override 238  ImmutableMap<V, K> map() { 239  return Inverse.this; 240  } 241  242  @Override 243  boolean isHashCodeFast() { 244  return true; 245  } 246  247  @Override 248  public int hashCode() { 249  return hashCode; 250  } 251  252  @Override 253  public UnmodifiableIterator<Entry<V, K>> iterator() { 254  return asList().iterator(); 255  } 256  257  @Override 258  public void forEach(Consumer<? super Entry<V, K>> action) { 259  asList().forEach(action); 260  } 261  262  @Override 263  ImmutableList<Entry<V, K>> createAsList() { 264  return new ImmutableAsList<Entry<V, K>>() { 265  @Override 266  public Entry<V, K> get(int index) { 267  Entry<K, V> entry = entries[index]; 268  return Maps.immutableEntry(entry.getValue(), entry.getKey()); 269  } 270  271  @Override 272  ImmutableCollection<Entry<V, K>> delegateCollection() { 273  return InverseEntrySet.this; 274  } 275  }; 276  } 277  } 278  279  @Override 280  boolean isPartialView() { 281  return false; 282  } 283  284  @Override 285  Object writeReplace() { 286  return new InverseSerializedForm<>(RegularImmutableBiMap.this); 287  } 288  } 289  290  private static class InverseSerializedForm<K, V> implements Serializable { 291  private final ImmutableBiMap<K, V> forward; 292  293  InverseSerializedForm(ImmutableBiMap<K, V> forward) { 294  this.forward = forward; 295  } 296  297  Object readResolve() { 298  return forward.inverse(); 299  } 300  301  private static final long serialVersionUID = 1; 302  } 303 }