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

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

Go to download

Google Collections Library is a suite of new collections and collection-related goodness for Java 5.0

There is a newer version: snapshot-20080530
Show newest version
/*
 * Copyright (C) 2007 Google Inc.
 *
 * 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 com.google.common.base.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * A general-purpose bimap implementation using any two backing {@code Map}
 * instances.
 *
 * @author Kevin Bourrillion
 * @author Mike Bostock
 */
class StandardBiMap extends ForwardingMap implements BiMap {
  /** Package-private constructor for creating a map-backed bimap. */
  StandardBiMap(Map forward, Map backward) {
    super(forward);
    checkArgument(forward.isEmpty());
    checkArgument(backward.isEmpty());
    inverse = new StandardBiMap(backward, this);
  }

  /** Private constructor for inverse bimap. */
  private StandardBiMap(Map backward, StandardBiMap forward) {
    super(backward);
    inverse = forward;
  }

  // Query Operations (optimizations)

  @Override public boolean containsValue(Object value) {
    return inverse.containsKey(value);
  }

  // Modification Operations

  @Override public V put(K key, V value) {
    return putInBothMaps(key, value, false);
  }

  public V forcePut(K key, V value) {
    return putInBothMaps(key, value, true);
  }

  private V putInBothMaps(K key, V value, boolean force) {
    boolean containedKey = containsKey(key);
    if (containedKey && Objects.equal(value, get(key))) {
      return value;
    }
    if (force) {
      inverse().remove(value);
    } else {
      checkArgument(!containsValue(value), "value already present: %s", value);
    }
    V oldValue = super.put(key, value);
    updateInverseMap(key, containedKey, oldValue, value);
    return oldValue;
  }

  private void updateInverseMap(
      K key, boolean containedKey, V oldValue, V newValue) {
    if (containedKey) {
      removeFromInverseMap(oldValue);
    }
    inverse.delegate().put(newValue, key);
  }

  @Override public V remove(Object key) {
    return containsKey(key) ? removeFromBothMaps(key) : null;
  }

  private V removeFromBothMaps(Object key) {
    V oldValue = super.remove(key);
    removeFromInverseMap(oldValue);
    return oldValue;
  }

  private void removeFromInverseMap(V oldValue) {
    inverse.delegate().remove(oldValue);
  }

  // Bulk Operations

  @Override public void putAll(Map map) {
    for (Entry entry : map.entrySet()) {
      put(entry.getKey(), entry.getValue());
    }
  }

  @Override public void clear() {
    super.clear();
    inverse.delegate().clear();
  }

  // Views

  // TODO: make this transient?
  private StandardBiMap inverse;

  public BiMap inverse() {
    return inverse;
  }

  private transient volatile Set keySet;

  @Override public Set keySet() {
    if (keySet == null) {
      keySet = new KeySet(super.keySet());
    }
    return keySet;
  }

  private class KeySet extends ForwardingSet {
    KeySet(Set keySet) {
      super(keySet);
    }

    @Override public void clear() {
      StandardBiMap.this.clear();
    }

    @Override public boolean remove(Object key) {
      if (!contains(key)) {
        return false;
      }
      removeFromBothMaps(key);
      return true;
    }

    @Override public boolean removeAll(Collection keysToRemove) {
      return removeAllImpl(this, keysToRemove);
    }

    @Override public boolean retainAll(Collection keysToRetain) {
      return retainAllImpl(this, keysToRetain);
    }

    @Override public Iterator iterator() {
      final Iterator> iterator
          = StandardBiMap.super.entrySet().iterator();
      return new Iterator() {
        Entry entry;

        public boolean hasNext() {
          return iterator.hasNext();
        }
        public K next() {
          entry = iterator.next();
          return entry.getKey();
        }
        public void remove() {
          iterator.remove();
          removeFromInverseMap(entry.getValue());
        }
      };
    }
  }

  @Override public Set values() {
    if (valueSet == null) {
      /*
       * We can almost reuse the inverse's keySet, except we have to fix the
       * iteration order so that it is consistent with the forward map.
       */
      valueSet = new ValueSet(inverse.keySet());
    }
    return valueSet;
  }

  private transient volatile Set valueSet;

  private class ValueSet extends ForwardingSet {
    ValueSet(Set values) {
      super(values);
    }

    @Override public Iterator iterator() {
      Iterator iterator
          = StandardBiMap.super.values().iterator();
      return new ForwardingIterator(iterator) {
        V valueToRemove;

        @Override public V next() {
          return valueToRemove = super.next();
        }

        @Override public void remove() {
          super.remove();
          removeFromInverseMap(valueToRemove);
        }
      };
    }

    @Override public Object[] toArray() {
      return toArrayImpl(this);
    }

    @Override public  T[] toArray(T[] array) {
      return toArrayImpl(this, array);
    }

    @Override public String toString() {
      return toStringImpl(this);
    }
  }

  private transient volatile Set> entrySet;

  @Override public Set> entrySet() {
    if (entrySet == null) {
      entrySet = new EntrySet(super.entrySet());
    }
    return entrySet;
  }

  private class EntrySet extends ForwardingSet> {
    EntrySet(Set> entrySet) {
      super(entrySet);
    }

    @Override public void clear() {
      StandardBiMap.this.clear();
    }

    @Override public boolean remove(Object object) {
      if (!super.remove(object)) {
        return false;
      }
      Entry entry = (Entry) object;
      inverse.delegate().remove(entry.getValue());
      return true;
    }

    @Override public Iterator> iterator() {
      return new ForwardingIterator>(super.iterator()) {
        Entry entry;

        @Override public Entry next() {
          entry = super.next();
          return new ForwardingMapEntry(entry) {
            @Override public V setValue(V value) {
              // similar to putInBothMaps, but set via entry
              if (Objects.equal(value, getValue())) {
                return value;
              }
              checkArgument(!containsValue(value),
                  "value already present: %s", value);
              V oldValue = super.setValue(value);
              updateInverseMap(getKey(), true, oldValue, value);
              return oldValue;
            }
          };
        }
        @Override public void remove() {
          super.remove();
          removeFromInverseMap(entry.getValue());
        }
      };
    }

    // See java.util.Collections.CheckedEntrySet for details on attacks.

    @Override public Object[] toArray() {
      return toArrayImpl(this);
    }
    @Override public  T[] toArray(T[] array) {
      return toArrayImpl(this, array);
    }
    @Override public boolean contains(Object o) {
      return Maps.containsEntryImpl(delegate(), o);
    }
    @Override public boolean containsAll(Collection c) {
      return containsAllImpl(this, c);
    }
    @Override public boolean removeAll(Collection c) {
      return removeAllImpl(this, c);
    }
    @Override public boolean retainAll(Collection c) {
      return retainAllImpl(this, c);
    }
  }

  private static final long serialVersionUID = 0x3EE04EBA918F30AFL;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy