Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2007 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.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 
 
 import java.util.Map;
 import java.util.Set;
 
 import  javax.annotation.Nullable;

A general-purpose bimap implementation using any two backing Map instances.

Note that this class contains equals() calls that keep it from supporting IdentityHashMap backing maps.

Author(s):
Kevin Bourrillion
Mike Bostock
 
 @GwtCompatible(emulated = true)
 abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
     implements BiMap<K, V>, Serializable {
 
   private transient Map<K, V> delegate;
   transient AbstractBiMap<V, K> inverse;

  
Package-private constructor for creating a map-backed bimap.
 
   AbstractBiMap(Map<K, V> forwardMap<V, K> backward) {
     setDelegates(forwardbackward);
   }

  
Private constructor for inverse bimap.
 
   private AbstractBiMap(Map<K, V> backwardAbstractBiMap<V, K> forward) {
      = backward;
      = forward;
   }
 
   @Override protected Map<K, V> delegate() {
     return ;
   }

  
Returns its input, or throws an exception if this is not a valid key.
 
   K checkKey(@Nullable K key) {
     return key;
   }

  
Returns its input, or throws an exception if this is not a valid value.
 
   V checkValue(@Nullable V value) {
     return value;
   }

  
Specifies the delegate maps going in each direction. Called by the constructor and by subclasses during deserialization.
 
   void setDelegates(Map<K, V> forwardMap<V, K> backward) {
     checkState( == null);
     checkState( == null);
     checkArgument(forward.isEmpty());
     checkArgument(backward.isEmpty());
     checkArgument(forward != backward);
      = forward;
      = new Inverse<V, K>(backwardthis);
   }
 
   void setInverse(AbstractBiMap<V, K> inverse) {
     this. = inverse;
   }
  // Query Operations (optimizations)
  @Override public boolean containsValue(Object value) {
    return .containsKey(value);
  }
  // Modification Operations
  @Override public V put(K key, V value) {
    return putInBothMaps(keyvaluefalse);
  }
  public V forcePut(K key, V value) {
    return putInBothMaps(keyvaluetrue);
  }
  private V putInBothMaps(@Nullable K key, @Nullable V valueboolean force) {
    checkKey(key);
    checkValue(value);
    boolean containedKey = containsKey(key);
    if (containedKey && Objects.equal(valueget(key))) {
      return value;
    }
    if (force) {
      inverse().remove(value);
    } else {
      checkArgument(!containsValue(value), "value already present: %s"value);
    }
    V oldValue = .put(keyvalue);
    updateInverseMap(keycontainedKeyoldValuevalue);
    return oldValue;
  }
  private void updateInverseMap(
      K keyboolean containedKey, V oldValue, V newValue) {
    if (containedKey) {
      removeFromInverseMap(oldValue);
    }
    ..put(newValuekey);
  }
  @Override public V remove(Object key) {
    return containsKey(key) ? removeFromBothMaps(key) : null;
  }
  private V removeFromBothMaps(Object key) {
    V oldValue = .remove(key);
    removeFromInverseMap(oldValue);
    return oldValue;
  }
  private void removeFromInverseMap(V oldValue) {
    ..remove(oldValue);
  }
  // Bulk Operations
  @Override public void putAll(Map<? extends K, ? extends V> map) {
    for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
      put(entry.getKey(), entry.getValue());
    }
  }
  @Override public void clear() {
    .clear();
  }
  // Views
  public BiMap<V, K> inverse() {
    return ;
  }
  private transient Set<K> keySet;
  @Override public Set<K> keySet() {
    Set<K> result = ;
    return (result == null) ?  = new KeySet() : result;
  }
  private class KeySet extends ForwardingSet<K> {
    @Override protected Set<K> delegate() {
      return .keySet();
    }
    @Override public void clear() {
      AbstractBiMap.this.clear();
    }
    @Override public boolean remove(Object key) {
      if (!contains(key)) {
        return false;
      }
      removeFromBothMaps(key);
      return true;
    }
    @Override public boolean removeAll(Collection<?> keysToRemove) {
      return standardRemoveAll(keysToRemove);
    }
    @Override public boolean retainAll(Collection<?> keysToRetain) {
      return standardRetainAll(keysToRetain);
    }
    @Override public Iterator<K> iterator() {
      return Maps.keyIterator(entrySet().iterator());
    }
  }
  private transient Set<V> valueSet;
  @Override public Set<V> values() {
    /*
     * 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.
     */
    Set<V> result = ;
    return (result == null) ?  = new ValueSet() : result;
  }
  private class ValueSet extends ForwardingSet<V> {
    final Set<V> valuesDelegate = .keySet();
    @Override protected Set<V> delegate() {
      return ;
    }
    @Override public Iterator<V> iterator() {
      return Maps.valueIterator(entrySet().iterator());
    }
    @Override public Object[] toArray() {
      return standardToArray();
    }
    @Override public <T> T[] toArray(T[] array) {
      return standardToArray(array);
    }
    @Override public String toString() {
      return standardToString();
    }
  }
  private transient Set<Entry<K, V>> entrySet;
  @Override public Set<Entry<K, V>> entrySet() {
    Set<Entry<K, V>> result = ;
    return (result == null) ?  = new EntrySet() : result;
  }
  private class EntrySet extends ForwardingSet<Entry<K, V>> {
    final Set<Entry<K, V>> esDelegate = .entrySet();
    @Override protected Set<Entry<K, V>> delegate() {
      return ;
    }
    @Override public void clear() {
      AbstractBiMap.this.clear();
    }
    @Override public boolean remove(Object object) {
      if (!.contains(object)) {
        return false;
      }
      // safe because esDelgate.contains(object).
      Entry<?, ?> entry = (Entry<?, ?>) object;
      ..remove(entry.getValue());
      /*
       * Remove the mapping in inverse before removing from esDelegate because
       * if entry is part of esDelegate, entry might be invalidated after the
       * mapping is removed from esDelegate.
       */
      .remove(entry);
      return true;
    }
    @Override public Iterator<Entry<K, V>> iterator() {
      final Iterator<Entry<K, V>> iterator = .iterator();
      return new Iterator<Entry<K, V>>() {
        Entry<K, V> entry;
        @Override public boolean hasNext() {
          return iterator.hasNext();
        }
        @Override public Entry<K, V> next() {
           = iterator.next();
          final Entry<K, V> finalEntry = ;
          return new ForwardingMapEntry<K, V>() {
            @Override protected Entry<K, V> delegate() {
              return finalEntry;
            }
            @Override public V setValue(V value) {
              // Preconditions keep the map and inverse consistent.
              checkState(contains(this), "entry no longer in map");
              // similar to putInBothMaps, but set via entry
              if (Objects.equal(valuegetValue())) {
                return value;
              }
              checkArgument(!containsValue(value),
                  "value already present: %s"value);
              V oldValue = finalEntry.setValue(value);
              checkState(Objects.equal(valueget(getKey())),
                  "entry no longer in map");
              updateInverseMap(getKey(), trueoldValuevalue);
              return oldValue;
            }
          };
        }
        @Override public void remove() {
          checkState( != null);
          V value = .getValue();
          iterator.remove();
          removeFromInverseMap(value);
        }
      };
    }
    // See java.util.Collections.CheckedEntrySet for details on attacks.
    @Override public Object[] toArray() {
      return standardToArray();
    }
    @Override public <T> T[] toArray(T[] array) {
      return standardToArray(array);
    }
    @Override public boolean contains(Object o) {
      return Maps.containsEntryImpl(delegate(), o);
    }
    @Override public boolean containsAll(Collection<?> c) {
      return standardContainsAll(c);
    }
    @Override public boolean removeAll(Collection<?> c) {
      return standardRemoveAll(c);
    }
    @Override public boolean retainAll(Collection<?> c) {
      return standardRetainAll(c);
    }
  }

  
The inverse of any other AbstractBiMap subclass.
  private static class Inverse<K, V> extends AbstractBiMap<K, V> {
    private Inverse(Map<K, V> backwardAbstractBiMap<V, K> forward) {
      super(backwardforward);
    }
    /*
     * Serialization stores the forward bimap, the inverse of this inverse.
     * Deserialization calls inverse() on the forward bimap and returns that
     * inverse.
     *
     * If a bimap and its inverse are serialized together, the deserialized
     * instances have inverse() methods that return the other.
     */
    @Override
    K checkKey(K key) {
      return .checkValue(key);
    }
    @Override
    V checkValue(V value) {
      return .checkKey(value);
    }

    

SerialData:
the forward bimap
    @GwtIncompatible("java.io.ObjectOuputStream")
    private void writeObject(ObjectOutputStream streamthrows IOException {
      stream.defaultWriteObject();
      stream.writeObject(inverse());
    }
    @GwtIncompatible("java.io.ObjectInputStream")
    @SuppressWarnings("unchecked"// reading data stored by writeObject
    private void readObject(ObjectInputStream stream)
        throws IOExceptionClassNotFoundException {
      stream.defaultReadObject();
      setInverse((AbstractBiMap<V, K>) stream.readObject());
    }
    @GwtIncompatible("Not needed in the emulated source.")
    Object readResolve() {
      return inverse().inverse();
    }
    @GwtIncompatible("Not needed in emulated source.")
    private static final long serialVersionUID = 0;
  }
  @GwtIncompatible("Not needed in emulated source.")
  private static final long serialVersionUID = 0;
New to GrepCode? Check out our FAQ X