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

com.google.common.collect.AbstractBiMap 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: 1.0
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.annotations.GwtCompatible;
import com.google.common.base.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;

/**
 * A general-purpose bimap implementation using any two backing {@code Map}
 * instances.
 *
 * 

Note that this class contains {@code equals()} calls that keep it from * supporting {@code IdentityHashMap} backing maps. * * @author Kevin Bourrillion * @author Mike Bostock */ @GwtCompatible abstract class AbstractBiMap extends ForwardingMap implements BiMap, Serializable { private transient Map delegate; private transient AbstractBiMap inverse; /** Package-private constructor for creating a map-backed bimap. */ AbstractBiMap(Map forward, Map backward) { setDelegates(forward, backward); } /** Private constructor for inverse bimap. */ private AbstractBiMap(Map backward, AbstractBiMap forward) { delegate = backward; inverse = forward; } @Override protected Map delegate() { return delegate; } /** * Specifies the delegate maps going in each direction. Called by the * constructor and by subclasses during deserialization. */ void setDelegates(Map forward, Map backward) { checkState(delegate == null); checkState(inverse == null); checkArgument(forward.isEmpty()); checkArgument(backward.isEmpty()); checkArgument(forward != backward); delegate = forward; inverse = new Inverse(backward, this); } void setInverse(AbstractBiMap inverse) { this.inverse = inverse; } // 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(@Nullable K key, @Nullable 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 = delegate.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 = delegate.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() { delegate.clear(); inverse.delegate.clear(); } // Views public BiMap inverse() { return inverse; } private transient volatile Set keySet; @Override public Set keySet() { Set result = keySet; return (result == null) ? keySet = new KeySet() : keySet; } private class KeySet extends ForwardingSet { @Override protected Set delegate() { return delegate.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 Iterators.removeAll(iterator(), keysToRemove); } @Override public boolean retainAll(Collection keysToRetain) { return Iterators.retainAll(iterator(), keysToRetain); } @Override public Iterator iterator() { final Iterator> iterator = delegate.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() { checkState(entry != null); V value = entry.getValue(); iterator.remove(); removeFromInverseMap(value); } }; } } private transient volatile Set valueSet; @Override public Set 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 result = valueSet; return (result == null) ? valueSet = new ValueSet() : valueSet; } private class ValueSet extends ForwardingSet { final Set valuesDelegate = inverse.keySet(); @Override protected Set delegate() { return valuesDelegate; } @Override public Iterator iterator() { final Iterator iterator = delegate.values().iterator(); return new Iterator() { V valueToRemove; /*@Override*/ public boolean hasNext() { return iterator.hasNext(); } /*@Override*/ public V next() { return valueToRemove = iterator.next(); } /*@Override*/ public void remove() { iterator.remove(); removeFromInverseMap(valueToRemove); } }; } @Override public Object[] toArray() { return ObjectArrays.toArrayImpl(this); } @Override public T[] toArray(T[] array) { return ObjectArrays.toArrayImpl(this, array); } @Override public String toString() { return Iterators.toString(iterator()); } } private transient volatile Set> entrySet; @Override public Set> entrySet() { Set> result = entrySet; return (result == null) ? entrySet = new EntrySet() : entrySet; } private class EntrySet extends ForwardingSet> { final Set> esDelegate = delegate.entrySet(); @Override protected Set> delegate() { return esDelegate; } @Override public void clear() { AbstractBiMap.this.clear(); } @Override public boolean remove(Object object) { if (!esDelegate.remove(object)) { return false; } Entry entry = (Entry) object; inverse.delegate.remove(entry.getValue()); return true; } @Override public Iterator> iterator() { final Iterator> iterator = esDelegate.iterator(); return new Iterator>() { Entry entry; /*@Override*/ public boolean hasNext() { return iterator.hasNext(); } /*@Override*/ public Entry next() { entry = iterator.next(); final Entry finalEntry = entry; return new ForwardingMapEntry() { @Override protected Entry 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(value, getValue())) { return value; } checkArgument(!containsValue(value), "value already present: %s", value); V oldValue = finalEntry.setValue(value); checkState(Objects.equal(value, get(getKey())), "entry no longer in map"); updateInverseMap(getKey(), true, oldValue, value); return oldValue; } }; } /*@Override*/ public void remove() { checkState(entry != null); V value = entry.getValue(); iterator.remove(); removeFromInverseMap(value); } }; } // See java.util.Collections.CheckedEntrySet for details on attacks. @Override public Object[] toArray() { return ObjectArrays.toArrayImpl(this); } @Override public T[] toArray(T[] array) { return ObjectArrays.toArrayImpl(this, array); } @Override public boolean contains(Object o) { return Maps.containsEntryImpl(delegate(), o); } @Override public boolean containsAll(Collection c) { return Collections2.containsAll(this, c); } @Override public boolean removeAll(Collection c) { return Iterators.removeAll(iterator(), c); } @Override public boolean retainAll(Collection c) { return Iterators.retainAll(iterator(), c); } } /** The inverse of any other {@code AbstractBiMap} subclass. */ private static class Inverse extends AbstractBiMap { private Inverse(Map backward, AbstractBiMap forward) { super(backward, forward); } /* * 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. */ /** * @serialData the forward bimap */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(inverse()); } @SuppressWarnings("unchecked") // reading data stored by writeObject private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); setInverse((AbstractBiMap) stream.readObject()); } Object readResolve() { return inverse().inverse(); } private static final long serialVersionUID = 0; } private static final long serialVersionUID = 0; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy