All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.RegularImmutableBiMap Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/*
 * Copyright (C) 2008 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.collect;

import static com.google.common.collect.CollectPreconditions.checkEntryNotNull;

import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.ImmutableMapEntry.TerminalEntry;

import java.io.Serializable;

import javax.annotation.Nullable;

/**
 * Bimap with two or more mappings.
 *
 * @author Louis Wasserman
 */
@GwtCompatible(serializable = true, emulated = true)
@SuppressWarnings("serial") // uses writeReplace(), not default serialization
class RegularImmutableBiMap extends ImmutableBiMap {
  
  static final double MAX_LOAD_FACTOR = 1.2;
  
  private final transient ImmutableMapEntry[] keyTable;
  private final transient ImmutableMapEntry[] valueTable;
  private final transient ImmutableMapEntry[] entries;
  private final transient int mask;
  private final transient int hashCode;
  
  RegularImmutableBiMap(TerminalEntry... entriesToAdd) {
    this(entriesToAdd.length, entriesToAdd);
  }
  
  /**
   * Constructor for RegularImmutableBiMap that takes as input an array of {@code TerminalEntry}
   * entries.  Assumes that these entries have already been checked for null.
   * 
   * 

This allows reuse of the entry objects from the array in the actual implementation. */ RegularImmutableBiMap(int n, TerminalEntry[] entriesToAdd) { int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); this.mask = tableSize - 1; ImmutableMapEntry[] keyTable = createEntryArray(tableSize); ImmutableMapEntry[] valueTable = createEntryArray(tableSize); ImmutableMapEntry[] entries = createEntryArray(n); int hashCode = 0; for (int i = 0; i < n; i++) { @SuppressWarnings("unchecked") TerminalEntry entry = (TerminalEntry) entriesToAdd[i]; K key = entry.getKey(); V value = entry.getValue(); int keyHash = key.hashCode(); int valueHash = value.hashCode(); int keyBucket = Hashing.smear(keyHash) & mask; int valueBucket = Hashing.smear(valueHash) & mask; ImmutableMapEntry nextInKeyBucket = keyTable[keyBucket]; for (ImmutableMapEntry keyEntry = nextInKeyBucket; keyEntry != null; keyEntry = keyEntry.getNextInKeyBucket()) { checkNoConflict(!key.equals(keyEntry.getKey()), "key", entry, keyEntry); } ImmutableMapEntry nextInValueBucket = valueTable[valueBucket]; for (ImmutableMapEntry valueEntry = nextInValueBucket; valueEntry != null; valueEntry = valueEntry.getNextInValueBucket()) { checkNoConflict(!value.equals(valueEntry.getValue()), "value", entry, valueEntry); } ImmutableMapEntry newEntry = (nextInKeyBucket == null && nextInValueBucket == null) ? entry : new NonTerminalBiMapEntry(entry, nextInKeyBucket, nextInValueBucket); keyTable[keyBucket] = newEntry; valueTable[valueBucket] = newEntry; entries[i] = newEntry; hashCode += keyHash ^ valueHash; } this.keyTable = keyTable; this.valueTable = valueTable; this.entries = entries; this.hashCode = hashCode; } /** * Constructor for RegularImmutableBiMap that makes no assumptions about the input entries. */ RegularImmutableBiMap(Entry[] entriesToAdd) { int n = entriesToAdd.length; int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); this.mask = tableSize - 1; ImmutableMapEntry[] keyTable = createEntryArray(tableSize); ImmutableMapEntry[] valueTable = createEntryArray(tableSize); ImmutableMapEntry[] entries = createEntryArray(n); int hashCode = 0; for (int i = 0; i < n; i++) { @SuppressWarnings("unchecked") Entry entry = (Entry) entriesToAdd[i]; K key = entry.getKey(); V value = entry.getValue(); checkEntryNotNull(key, value); int keyHash = key.hashCode(); int valueHash = value.hashCode(); int keyBucket = Hashing.smear(keyHash) & mask; int valueBucket = Hashing.smear(valueHash) & mask; ImmutableMapEntry nextInKeyBucket = keyTable[keyBucket]; for (ImmutableMapEntry keyEntry = nextInKeyBucket; keyEntry != null; keyEntry = keyEntry.getNextInKeyBucket()) { checkNoConflict(!key.equals(keyEntry.getKey()), "key", entry, keyEntry); } ImmutableMapEntry nextInValueBucket = valueTable[valueBucket]; for (ImmutableMapEntry valueEntry = nextInValueBucket; valueEntry != null; valueEntry = valueEntry.getNextInValueBucket()) { checkNoConflict(!value.equals(valueEntry.getValue()), "value", entry, valueEntry); } ImmutableMapEntry newEntry = (nextInKeyBucket == null && nextInValueBucket == null) ? new TerminalEntry(key, value) : new NonTerminalBiMapEntry(key, value, nextInKeyBucket, nextInValueBucket); keyTable[keyBucket] = newEntry; valueTable[valueBucket] = newEntry; entries[i] = newEntry; hashCode += keyHash ^ valueHash; } this.keyTable = keyTable; this.valueTable = valueTable; this.entries = entries; this.hashCode = hashCode; } private static final class NonTerminalBiMapEntry extends ImmutableMapEntry { @Nullable private final ImmutableMapEntry nextInKeyBucket; @Nullable private final ImmutableMapEntry nextInValueBucket; NonTerminalBiMapEntry(K key, V value, @Nullable ImmutableMapEntry nextInKeyBucket, @Nullable ImmutableMapEntry nextInValueBucket) { super(key, value); this.nextInKeyBucket = nextInKeyBucket; this.nextInValueBucket = nextInValueBucket; } NonTerminalBiMapEntry(ImmutableMapEntry contents, @Nullable ImmutableMapEntry nextInKeyBucket, @Nullable ImmutableMapEntry nextInValueBucket) { super(contents); this.nextInKeyBucket = nextInKeyBucket; this.nextInValueBucket = nextInValueBucket; } @Override @Nullable ImmutableMapEntry getNextInKeyBucket() { return nextInKeyBucket; } @Override @Nullable ImmutableMapEntry getNextInValueBucket() { return nextInValueBucket; } } @SuppressWarnings("unchecked") private static ImmutableMapEntry[] createEntryArray(int length) { return new ImmutableMapEntry[length]; } @Override @Nullable public V get(@Nullable Object key) { if (key == null) { return null; } int bucket = Hashing.smear(key.hashCode()) & mask; for (ImmutableMapEntry entry = keyTable[bucket]; entry != null; entry = entry.getNextInKeyBucket()) { if (key.equals(entry.getKey())) { return entry.getValue(); } } return null; } @Override ImmutableSet> createEntrySet() { return new ImmutableMapEntrySet() { @Override ImmutableMap map() { return RegularImmutableBiMap.this; } @Override public UnmodifiableIterator> iterator() { return asList().iterator(); } @Override ImmutableList> createAsList() { return new RegularImmutableAsList>(this, entries); } @Override boolean isHashCodeFast() { return true; } @Override public int hashCode() { return hashCode; } }; } @Override boolean isPartialView() { return false; } @Override public int size() { return entries.length; } private transient ImmutableBiMap inverse; @Override public ImmutableBiMap inverse() { ImmutableBiMap result = inverse; return (result == null) ? inverse = new Inverse() : result; } private final class Inverse extends ImmutableBiMap { @Override public int size() { return inverse().size(); } @Override public ImmutableBiMap inverse() { return RegularImmutableBiMap.this; } @Override public K get(@Nullable Object value) { if (value == null) { return null; } int bucket = Hashing.smear(value.hashCode()) & mask; for (ImmutableMapEntry entry = valueTable[bucket]; entry != null; entry = entry.getNextInValueBucket()) { if (value.equals(entry.getValue())) { return entry.getKey(); } } return null; } @Override ImmutableSet> createEntrySet() { return new InverseEntrySet(); } final class InverseEntrySet extends ImmutableMapEntrySet { @Override ImmutableMap map() { return Inverse.this; } @Override boolean isHashCodeFast() { return true; } @Override public int hashCode() { return hashCode; } @Override public UnmodifiableIterator> iterator() { return asList().iterator(); } @Override ImmutableList> createAsList() { return new ImmutableAsList>() { @Override public Entry get(int index) { Entry entry = entries[index]; return Maps.immutableEntry(entry.getValue(), entry.getKey()); } @Override ImmutableCollection> delegateCollection() { return InverseEntrySet.this; } }; } } @Override boolean isPartialView() { return false; } @Override Object writeReplace() { return new InverseSerializedForm(RegularImmutableBiMap.this); } } private static class InverseSerializedForm implements Serializable { private final ImmutableBiMap forward; InverseSerializedForm(ImmutableBiMap forward) { this.forward = forward; } Object readResolve() { return forward.inverse(); } private static final long serialVersionUID = 1; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy