/*
 * 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.checkNotNull;
import static com.google.common.collect.CollectPreconditions.checkRemove;

import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.RandomAccess;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;

import javax.annotation.Nullable;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.Maps.ImprovedAbstractMap;

/**
 * Basic implementation of the {@link Multimap} interface. This class represents
 * a multimap as a map that associates each key with a collection of values. All
 * methods of {@link Multimap} are supported, including those specified as
 * optional in the interface.
 *
 * <p>
 * To implement a multimap, a subclass must define the method
 * {@link #createCollection()}, which creates an empty collection of values for
 * a key.
 *
 * <p>
 * The multimap constructor takes a map that has a single entry for each
 * distinct key. When you insert a key-value pair with a key that isn't already
 * in the multimap, {@code AbstractMapBasedMultimap} calls
 * {@link #createCollection()} to create the collection of values for that key.
 * The subclass should not call {@link #createCollection()} directly, and a new
 * instance should be created every time the method is called.
 *
 * <p>
 * For example, the subclass could pass a {@link java.util.TreeMap} during
 * construction, and {@link #createCollection()} could return a
 * {@link java.util.TreeSet}, in which case the multimap's iterators would
 * propagate through the keys and values in sorted order.
 *
 * <p>
 * Keys and values may be null, as long as the underlying collection classes
 * support null elements.
 *
 * <p>
 * The collections created by {@link #createCollection()} may or may not allow
 * duplicates. If the collection, such as a {@link Set}, does not support
 * duplicates, an added key-value pair will replace an existing pair with the
 * same key and value, if such a pair is present. With collections like
 * {@link List} that allow duplicates, the collection will keep the existing
 * key-value pairs while adding a new pair.
 *
 * <p>
 * This class is not threadsafe when any concurrent operations update the
 * multimap, even if the underlying map and {@link #createCollection()} method
 * return threadsafe classes. Concurrent read operations will work correctly. To
 * allow concurrent update operations, wrap your multimap with a call to
 * {@link Multimaps#synchronizedMultimap}.
 *
 * <p>
 * For serialization to work, the subclass must specify explicit
 * {@code readObject} and {@code writeObject} methods.
 *
 * @author Jared Levy
 * @author Louis Wasserman
 */
@GwtCompatible(emulated = true)
abstract class AbstractMapBasedMultimap<K, V> extends AbstractMultimap<K, V> implements Serializable {
	/*
	 * Here's an outline of the overall design.
	 *
	 * The map variable contains the collection of values associated with each key.
	 * When a key-value pair is added to a multimap that didn't previously contain
	 * any values for that key, a new collection generated by createCollection is
	 * added to the map. That same collection instance remains in the map as long as
	 * the multimap has any values for the key. If all values for the key are
	 * removed, the key and collection are removed from the map.
	 *
	 * The get method returns a WrappedCollection, which decorates the collection in
	 * the map (if the key is present) or an empty collection (if the key is not
	 * present). When the collection delegate in the WrappedCollection is empty, the
	 * multimap may contain subsequently added values for that key. To handle that
	 * situation, the WrappedCollection checks whether map contains an entry for the
	 * provided key, and if so replaces the delegate.
	 */

	private transient Map<K, Collection<V>> map;
	private transient int totalSize;

	/**
	 * Creates a new multimap that uses the provided map.
	 *
	 * @param map place to store the mapping from each key to its corresponding
	 *            values
	 * @throws IllegalArgumentException if {@code map} is not empty
	 */
	protected AbstractMapBasedMultimap(Map<K, Collection<V>> map) {
		checkArgument(map.isEmpty());
		this.map = map;
	}

	/** Used during deserialization only. */
	final void setMap(Map<K, Collection<V>> map) {
		this.map = map;
		totalSize = 0;
		for (Collection<V> values : map.values()) {
			checkArgument(!values.isEmpty());
			totalSize += values.size();
		}
	}

	/**
	 * Creates an unmodifiable, empty collection of values.
	 *
	 * <p>
	 * This is used in {@link #removeAll} on an empty key.
	 */
	Collection<V> createUnmodifiableEmptyCollection() {
		return unmodifiableCollectionSubclass(createCollection());
	}

	/**
	 * Creates the collection of values for a single key.
	 *
	 * <p>
	 * Collections with weak, soft, or phantom references are not supported. Each
	 * call to {@code createCollection} should create a new instance.
	 *
	 * <p>
	 * The returned collection class determines whether duplicate key-value pairs
	 * are allowed.
	 *
	 * @return an empty collection of values
	 */
	abstract Collection<V> createCollection();

	/**
	 * Creates the collection of values for an explicitly provided key. By default,
	 * it simply calls {@link #createCollection()}, which is the correct behavior
	 * for most implementations. The {@link LinkedHashMultimap} class overrides it.
	 *
	 * @param key key to associate with values in the collection
	 * @return an empty collection of values
	 */
	Collection<V> createCollection(@Nullable K key) {
		return createCollection();
	}

	Map<K, Collection<V>> backingMap() {
		return map;
	}

	// Query Operations

	@Override
	public int size() {
		return totalSize;
	}

	@Override
	public boolean containsKey(@Nullable Object key) {
		return map.containsKey(key);
	}

	// Modification Operations

	@Override
	public boolean put(@Nullable K key, @Nullable V value) {
		Collection<V> collection = map.get(key);
		if (collection == null) {
			collection = createCollection(key);
			if (collection.add(value)) {
				totalSize++;
				map.put(key, collection);
				return true;
			} else {
				throw new AssertionError("New Collection violated the Collection spec");
			}
		} else if (collection.add(value)) {
			totalSize++;
			return true;
		} else {
			return false;
		}
	}

	private Collection<V> getOrCreateCollection(@Nullable K key) {
		Collection<V> collection = map.get(key);
		if (collection == null) {
			collection = createCollection(key);
			map.put(key, collection);
		}
		return collection;
	}

	// Bulk Operations

	/**
	 * {@inheritDoc}
	 *
	 * <p>
	 * The returned collection is immutable.
	 */
	@Override
	public Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
		Iterator<? extends V> iterator = values.iterator();
		if (!iterator.hasNext()) {
			return removeAll(key);
		}

		// TODO(user): investigate atomic failure?
		Collection<V> collection = getOrCreateCollection(key);
		Collection<V> oldValues = createCollection();
		oldValues.addAll(collection);

		totalSize -= collection.size();
		collection.clear();

		while (iterator.hasNext()) {
			if (collection.add(iterator.next())) {
				totalSize++;
			}
		}

		return unmodifiableCollectionSubclass(oldValues);
	}

	/**
	 * {@inheritDoc}
	 *
	 * <p>
	 * The returned collection is immutable.
	 */
	@Override
	public Collection<V> removeAll(@Nullable Object key) {
		Collection<V> collection = map.remove(key);

		if (collection == null) {
			return createUnmodifiableEmptyCollection();
		}

		Collection<V> output = createCollection();
		output.addAll(collection);
		totalSize -= collection.size();
		collection.clear();

		return unmodifiableCollectionSubclass(output);
	}

	Collection<V> unmodifiableCollectionSubclass(Collection<V> collection) {
		// We don't deal with NavigableSet here yet for GWT reasons -- instead,
		// non-GWT TreeMultimap explicitly overrides this and uses NavigableSet.
		if (collection instanceof SortedSet) {
			return Collections.unmodifiableSortedSet((SortedSet<V>) collection);
		} else if (collection instanceof Set) {
			return Collections.unmodifiableSet((Set<V>) collection);
		} else if (collection instanceof List) {
			return Collections.unmodifiableList((List<V>) collection);
		} else {
			return Collections.unmodifiableCollection(collection);
		}
	}

	@Override
	public void clear() {
		// Clear each collection, to make previously returned collections empty.
		for (Collection<V> collection : map.values()) {
			collection.clear();
		}
		map.clear();
		totalSize = 0;
	}

	// Views

	/**
	 * {@inheritDoc}
	 *
	 * <p>
	 * The returned collection is not serializable.
	 */
	@Override
	public Collection<V> get(@Nullable K key) {
		Collection<V> collection = map.get(key);
		if (collection == null) {
			collection = createCollection(key);
		}
		return wrapCollection(key, collection);
	}

	/**
	 * Generates a decorated collection that remains consistent with the values in
	 * the multimap for the provided key. Changes to the multimap may alter the
	 * returned collection, and vice versa.
	 */
	Collection<V> wrapCollection(@Nullable K key, Collection<V> collection) {
		// We don't deal with NavigableSet here yet for GWT reasons -- instead,
		// non-GWT TreeMultimap explicitly overrides this and uses NavigableSet.
		if (collection instanceof SortedSet) {
			return new WrappedSortedSet(key, (SortedSet<V>) collection, null);
		} else if (collection instanceof Set) {
			return new WrappedSet(key, (Set<V>) collection);
		} else if (collection instanceof List) {
			return wrapList(key, (List<V>) collection, null);
		} else {
			return new WrappedCollection(key, collection, null);
		}
	}

	private List<V> wrapList(@Nullable K key, List<V> list, @Nullable WrappedCollection ancestor) {
		return (list instanceof RandomAccess) ? new RandomAccessWrappedList(key, list, ancestor)
				: new WrappedList(key, list, ancestor);
	}

	/**
	 * Collection decorator that stays in sync with the multimap values for a key.
	 * There are two kinds of wrapped collections: full and subcollections. Both
	 * have a delegate pointing to the underlying collection class.
	 *
	 * <p>
	 * Full collections, identified by a null ancestor field, contain all multimap
	 * values for a given key. Its delegate is a value in
	 * {@link AbstractMapBasedMultimap#map} whenever the delegate is non-empty. The
	 * {@code
	 * refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} methods ensure
	 * that the {@code WrappedCollection} and map remain consistent.
	 *
	 * <p>
	 * A subcollection, such as a sublist, contains some of the values for a given
	 * key. Its ancestor field points to the full wrapped collection with all values
	 * for the key. The subcollection {@code refreshIfEmpty}, {@code
	 * removeIfEmpty}, and {@code addToMap} methods call the corresponding methods
	 * of the full wrapped collection.
	 */
	private class WrappedCollection extends AbstractCollection<V> {
		final K key;
		Collection<V> delegate;
		final WrappedCollection ancestor;
		final Collection<V> ancestorDelegate;

		WrappedCollection(@Nullable K key, Collection<V> delegate, @Nullable WrappedCollection ancestor) {
			this.key = key;
			this.delegate = delegate;
			this.ancestor = ancestor;
			this.ancestorDelegate = (ancestor == null) ? null : ancestor.getDelegate();
		}

		/**
		 * If the delegate collection is empty, but the multimap has values for the key,
		 * replace the delegate with the new collection for the key.
		 *
		 * <p>
		 * For a subcollection, refresh its ancestor and validate that the ancestor
		 * delegate hasn't changed.
		 */
		void refreshIfEmpty() {
			if (ancestor != null) {
				ancestor.refreshIfEmpty();
				if (ancestor.getDelegate() != ancestorDelegate) {
					throw new ConcurrentModificationException();
				}
			} else if (delegate.isEmpty()) {
				Collection<V> newDelegate = map.get(key);
				if (newDelegate != null) {
					delegate = newDelegate;
				}
			}
		}

		/**
		 * If collection is empty, remove it from
		 * {@code AbstractMapBasedMultimap.this.map}. For subcollections, check whether
		 * the ancestor collection is empty.
		 */
		void removeIfEmpty() {
			if (ancestor != null) {
				ancestor.removeIfEmpty();
			} else if (delegate.isEmpty()) {
				map.remove(key);
			}
		}

		K getKey() {
			return key;
		}

		/**
		 * Add the delegate to the map. Other {@code WrappedCollection} methods should
		 * call this method after adding elements to a previously empty collection.
		 *
		 * <p>
		 * Subcollection add the ancestor's delegate instead.
		 */
		void addToMap() {
			if (ancestor != null) {
				ancestor.addToMap();
			} else {
				map.put(key, delegate);
			}
		}

		@Override
		public int size() {
			refreshIfEmpty();
			return delegate.size();
		}

		@Override
		public boolean equals(@Nullable Object object) {
			if (object == this) {
				return true;
			}
			refreshIfEmpty();
			return delegate.equals(object);
		}

		@Override
		public int hashCode() {
			refreshIfEmpty();
			return delegate.hashCode();
		}

		@Override
		public String toString() {
			refreshIfEmpty();
			return delegate.toString();
		}

		Collection<V> getDelegate() {
			return delegate;
		}

		@Override
		public Iterator<V> iterator() {
			refreshIfEmpty();
			return new WrappedIterator();
		}

		/** Collection iterator for {@code WrappedCollection}. */
		class WrappedIterator implements Iterator<V> {
			final Iterator<V> delegateIterator;
			final Collection<V> originalDelegate = delegate;

			WrappedIterator() {
				delegateIterator = iteratorOrListIterator(delegate);
			}

			WrappedIterator(Iterator<V> delegateIterator) {
				this.delegateIterator = delegateIterator;
			}

			/**
			 * If the delegate changed since the iterator was created, the iterator is no
			 * longer valid.
			 */
			void validateIterator() {
				refreshIfEmpty();
				if (delegate != originalDelegate) {
					throw new ConcurrentModificationException();
				}
			}

			@Override
			public boolean hasNext() {
				validateIterator();
				return delegateIterator.hasNext();
			}

			@Override
			public V next() {
				validateIterator();
				return delegateIterator.next();
			}

			@Override
			public void remove() {
				delegateIterator.remove();
				totalSize--;
				removeIfEmpty();
			}

			Iterator<V> getDelegateIterator() {
				validateIterator();
				return delegateIterator;
			}
		}

		@Override
		public boolean add(V value) {
			refreshIfEmpty();
			boolean wasEmpty = delegate.isEmpty();
			boolean changed = delegate.add(value);
			if (changed) {
				totalSize++;
				if (wasEmpty) {
					addToMap();
				}
			}
			return changed;
		}

		WrappedCollection getAncestor() {
			return ancestor;
		}

		// The following methods are provided for better performance.

		@Override
		public boolean addAll(Collection<? extends V> collection) {
			if (collection.isEmpty()) {
				return false;
			}
			int oldSize = size(); // calls refreshIfEmpty
			boolean changed = delegate.addAll(collection);
			if (changed) {
				int newSize = delegate.size();
				totalSize += (newSize - oldSize);
				if (oldSize == 0) {
					addToMap();
				}
			}
			return changed;
		}

		@Override
		public boolean contains(Object o) {
			refreshIfEmpty();
			return delegate.contains(o);
		}

		@Override
		public boolean containsAll(Collection<?> c) {
			refreshIfEmpty();
			return delegate.containsAll(c);
		}

		@Override
		public void clear() {
			int oldSize = size(); // calls refreshIfEmpty
			if (oldSize == 0) {
				return;
			}
			delegate.clear();
			totalSize -= oldSize;
			removeIfEmpty(); // maybe shouldn't be removed if this is a sublist
		}

		@Override
		public boolean remove(Object o) {
			refreshIfEmpty();
			boolean changed = delegate.remove(o);
			if (changed) {
				totalSize--;
				removeIfEmpty();
			}
			return changed;
		}

		@Override
		public boolean removeAll(Collection<?> c) {
			if (c.isEmpty()) {
				return false;
			}
			int oldSize = size(); // calls refreshIfEmpty
			boolean changed = delegate.removeAll(c);
			if (changed) {
				int newSize = delegate.size();
				totalSize += (newSize - oldSize);
				removeIfEmpty();
			}
			return changed;
		}

		@Override
		public boolean retainAll(Collection<?> c) {
			checkNotNull(c);
			int oldSize = size(); // calls refreshIfEmpty
			boolean changed = delegate.retainAll(c);
			if (changed) {
				int newSize = delegate.size();
				totalSize += (newSize - oldSize);
				removeIfEmpty();
			}
			return changed;
		}
	}

	private Iterator<V> iteratorOrListIterator(Collection<V> collection) {
		return (collection instanceof List) ? ((List<V>) collection).listIterator() : collection.iterator();
	}

	/** Set decorator that stays in sync with the multimap values for a key. */
	private class WrappedSet extends WrappedCollection implements Set<V> {
		WrappedSet(@Nullable K key, Set<V> delegate) {
			super(key, delegate, null);
		}

		@Override
		public boolean removeAll(Collection<?> c) {
			if (c.isEmpty()) {
				return false;
			}
			int oldSize = size(); // calls refreshIfEmpty

			// Guava issue 1013: AbstractSet and most JDK set implementations are
			// susceptible to quadratic removeAll performance on lists;
			// use a slightly smarter implementation here
			boolean changed = Sets.removeAllImpl((Set<V>) delegate, c);
			if (changed) {
				int newSize = delegate.size();
				totalSize += (newSize - oldSize);
				removeIfEmpty();
			}
			return changed;
		}
	}

	/**
	 * SortedSet decorator that stays in sync with the multimap values for a key.
	 */
	private class WrappedSortedSet extends WrappedCollection implements SortedSet<V> {
		WrappedSortedSet(@Nullable K key, SortedSet<V> delegate, @Nullable WrappedCollection ancestor) {
			super(key, delegate, ancestor);
		}

		SortedSet<V> getSortedSetDelegate() {
			return (SortedSet<V>) getDelegate();
		}

		@Override
		public Comparator<? super V> comparator() {
			return getSortedSetDelegate().comparator();
		}

		@Override
		public V first() {
			refreshIfEmpty();
			return getSortedSetDelegate().first();
		}

		@Override
		public V last() {
			refreshIfEmpty();
			return getSortedSetDelegate().last();
		}

		@Override
		public SortedSet<V> headSet(V toElement) {
			refreshIfEmpty();
			return new WrappedSortedSet(getKey(), getSortedSetDelegate().headSet(toElement),
					(getAncestor() == null) ? this : getAncestor());
		}

		@Override
		public SortedSet<V> subSet(V fromElement, V toElement) {
			refreshIfEmpty();
			return new WrappedSortedSet(getKey(), getSortedSetDelegate().subSet(fromElement, toElement),
					(getAncestor() == null) ? this : getAncestor());
		}

		@Override
		public SortedSet<V> tailSet(V fromElement) {
			refreshIfEmpty();
			return new WrappedSortedSet(getKey(), getSortedSetDelegate().tailSet(fromElement),
					(getAncestor() == null) ? this : getAncestor());
		}
	}

	@GwtIncompatible("NavigableSet")
	class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet<V> {
		WrappedNavigableSet(@Nullable K key, NavigableSet<V> delegate, @Nullable WrappedCollection ancestor) {
			super(key, delegate, ancestor);
		}

		@Override
		NavigableSet<V> getSortedSetDelegate() {
			return (NavigableSet<V>) super.getSortedSetDelegate();
		}

		@Override
		public V lower(V v) {
			return getSortedSetDelegate().lower(v);
		}

		@Override
		public V floor(V v) {
			return getSortedSetDelegate().floor(v);
		}

		@Override
		public V ceiling(V v) {
			return getSortedSetDelegate().ceiling(v);
		}

		@Override
		public V higher(V v) {
			return getSortedSetDelegate().higher(v);
		}

		@Override
		public V pollFirst() {
			return Iterators.pollNext(iterator());
		}

		@Override
		public V pollLast() {
			return Iterators.pollNext(descendingIterator());
		}

		private NavigableSet<V> wrap(NavigableSet<V> wrapped) {
			return new WrappedNavigableSet(key, wrapped, (getAncestor() == null) ? this : getAncestor());
		}

		@Override
		public NavigableSet<V> descendingSet() {
			return wrap(getSortedSetDelegate().descendingSet());
		}

		@Override
		public Iterator<V> descendingIterator() {
			return new WrappedIterator(getSortedSetDelegate().descendingIterator());
		}

		@Override
		public NavigableSet<V> subSet(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) {
			return wrap(getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive));
		}

		@Override
		public NavigableSet<V> headSet(V toElement, boolean inclusive) {
			return wrap(getSortedSetDelegate().headSet(toElement, inclusive));
		}

		@Override
		public NavigableSet<V> tailSet(V fromElement, boolean inclusive) {
			return wrap(getSortedSetDelegate().tailSet(fromElement, inclusive));
		}
	}

	/** List decorator that stays in sync with the multimap values for a key. */
	private class WrappedList extends WrappedCollection implements List<V> {
		WrappedList(@Nullable K key, List<V> delegate, @Nullable WrappedCollection ancestor) {
			super(key, delegate, ancestor);
		}

		List<V> getListDelegate() {
			return (List<V>) getDelegate();
		}

		@Override
		public boolean addAll(int index, Collection<? extends V> c) {
			if (c.isEmpty()) {
				return false;
			}
			int oldSize = size(); // calls refreshIfEmpty
			boolean changed = getListDelegate().addAll(index, c);
			if (changed) {
				int newSize = getDelegate().size();
				totalSize += (newSize - oldSize);
				if (oldSize == 0) {
					addToMap();
				}
			}
			return changed;
		}

		@Override
		public V get(int index) {
			refreshIfEmpty();
			return getListDelegate().get(index);
		}

		@Override
		public V set(int index, V element) {
			refreshIfEmpty();
			return getListDelegate().set(index, element);
		}

		@Override
		public void add(int index, V element) {
			refreshIfEmpty();
			boolean wasEmpty = getDelegate().isEmpty();
			getListDelegate().add(index, element);
			totalSize++;
			if (wasEmpty) {
				addToMap();
			}
		}

		@Override
		public V remove(int index) {
			refreshIfEmpty();
			V value = getListDelegate().remove(index);
			totalSize--;
			removeIfEmpty();
			return value;
		}

		@Override
		public int indexOf(Object o) {
			refreshIfEmpty();
			return getListDelegate().indexOf(o);
		}

		@Override
		public int lastIndexOf(Object o) {
			refreshIfEmpty();
			return getListDelegate().lastIndexOf(o);
		}

		@Override
		public ListIterator<V> listIterator() {
			refreshIfEmpty();
			return new WrappedListIterator();
		}

		@Override
		public ListIterator<V> listIterator(int index) {
			refreshIfEmpty();
			return new WrappedListIterator(index);
		}

		@Override
		public List<V> subList(int fromIndex, int toIndex) {
			refreshIfEmpty();
			return wrapList(getKey(), getListDelegate().subList(fromIndex, toIndex),
					(getAncestor() == null) ? this : getAncestor());
		}

		/** ListIterator decorator. */
		private class WrappedListIterator extends WrappedIterator implements ListIterator<V> {
			WrappedListIterator() {
			}

			public WrappedListIterator(int index) {
				super(getListDelegate().listIterator(index));
			}

			private ListIterator<V> getDelegateListIterator() {
				return (ListIterator<V>) getDelegateIterator();
			}

			@Override
			public boolean hasPrevious() {
				return getDelegateListIterator().hasPrevious();
			}

			@Override
			public V previous() {
				return getDelegateListIterator().previous();
			}

			@Override
			public int nextIndex() {
				return getDelegateListIterator().nextIndex();
			}

			@Override
			public int previousIndex() {
				return getDelegateListIterator().previousIndex();
			}

			@Override
			public void set(V value) {
				getDelegateListIterator().set(value);
			}

			@Override
			public void add(V value) {
				boolean wasEmpty = isEmpty();
				getDelegateListIterator().add(value);
				totalSize++;
				if (wasEmpty) {
					addToMap();
				}
			}
		}
	}

	/**
	 * List decorator that stays in sync with the multimap values for a key and
	 * supports rapid random access.
	 */
	private class RandomAccessWrappedList extends WrappedList implements RandomAccess {
		RandomAccessWrappedList(@Nullable K key, List<V> delegate, @Nullable WrappedCollection ancestor) {
			super(key, delegate, ancestor);
		}
	}

	@Override
	Set<K> createKeySet() {
		// TreeMultimap uses NavigableKeySet explicitly, but we don't handle that here
		// for GWT
		// compatibility reasons
		return (map instanceof SortedMap) ? new SortedKeySet((SortedMap<K, Collection<V>>) map) : new KeySet(map);
	}

	private class KeySet extends Maps.KeySet<K, Collection<V>> {
		KeySet(final Map<K, Collection<V>> subMap) {
			super(subMap);
		}

		@Override
		public Iterator<K> iterator() {
			final Iterator<Map.Entry<K, Collection<V>>> entryIterator = map().entrySet().iterator();
			return new Iterator<K>() {
				Map.Entry<K, Collection<V>> entry;

				@Override
				public boolean hasNext() {
					return entryIterator.hasNext();
				}

				@Override
				public K next() {
					entry = entryIterator.next();
					return entry.getKey();
				}

				@Override
				public void remove() {
					checkRemove(entry != null);
					Collection<V> collection = entry.getValue();
					entryIterator.remove();
					totalSize -= collection.size();
					collection.clear();
				}
			};
		}

		// The following methods are included for better performance.

		@Override
		public boolean remove(Object key) {
			int count = 0;
			Collection<V> collection = map().remove(key);
			if (collection != null) {
				count = collection.size();
				collection.clear();
				totalSize -= count;
			}
			return count > 0;
		}

		@Override
		public void clear() {
			Iterators.clear(iterator());
		}

		@Override
		public boolean containsAll(Collection<?> c) {
			return map().keySet().containsAll(c);
		}

		@Override
		public boolean equals(@Nullable Object object) {
			return this == object || this.map().keySet().equals(object);
		}

		@Override
		public int hashCode() {
			return map().keySet().hashCode();
		}
	}

	private class SortedKeySet extends KeySet implements SortedSet<K> {

		SortedKeySet(SortedMap<K, Collection<V>> subMap) {
			super(subMap);
		}

		SortedMap<K, Collection<V>> sortedMap() {
			return (SortedMap<K, Collection<V>>) super.map();
		}

		@Override
		public Comparator<? super K> comparator() {
			return sortedMap().comparator();
		}

		@Override
		public K first() {
			return sortedMap().firstKey();
		}

		@Override
		public SortedSet<K> headSet(K toElement) {
			return new SortedKeySet(sortedMap().headMap(toElement));
		}

		@Override
		public K last() {
			return sortedMap().lastKey();
		}

		@Override
		public SortedSet<K> subSet(K fromElement, K toElement) {
			return new SortedKeySet(sortedMap().subMap(fromElement, toElement));
		}

		@Override
		public SortedSet<K> tailSet(K fromElement) {
			return new SortedKeySet(sortedMap().tailMap(fromElement));
		}
	}

	@GwtIncompatible("NavigableSet")
	class NavigableKeySet extends SortedKeySet implements NavigableSet<K> {
		NavigableKeySet(NavigableMap<K, Collection<V>> subMap) {
			super(subMap);
		}

		@Override
		NavigableMap<K, Collection<V>> sortedMap() {
			return (NavigableMap<K, Collection<V>>) super.sortedMap();
		}

		@Override
		public K lower(K k) {
			return sortedMap().lowerKey(k);
		}

		@Override
		public K floor(K k) {
			return sortedMap().floorKey(k);
		}

		@Override
		public K ceiling(K k) {
			return sortedMap().ceilingKey(k);
		}

		@Override
		public K higher(K k) {
			return sortedMap().higherKey(k);
		}

		@Override
		public K pollFirst() {
			return Iterators.pollNext(iterator());
		}

		@Override
		public K pollLast() {
			return Iterators.pollNext(descendingIterator());
		}

		@Override
		public NavigableSet<K> descendingSet() {
			return new NavigableKeySet(sortedMap().descendingMap());
		}

		@Override
		public Iterator<K> descendingIterator() {
			return descendingSet().iterator();
		}

		@Override
		public NavigableSet<K> headSet(K toElement) {
			return headSet(toElement, false);
		}

		@Override
		public NavigableSet<K> headSet(K toElement, boolean inclusive) {
			return new NavigableKeySet(sortedMap().headMap(toElement, inclusive));
		}

		@Override
		public NavigableSet<K> subSet(K fromElement, K toElement) {
			return subSet(fromElement, true, toElement, false);
		}

		@Override
		public NavigableSet<K> subSet(K fromElement, boolean fromInclusive, K toElement, boolean toInclusive) {
			return new NavigableKeySet(sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive));
		}

		@Override
		public NavigableSet<K> tailSet(K fromElement) {
			return tailSet(fromElement, true);
		}

		@Override
		public NavigableSet<K> tailSet(K fromElement, boolean inclusive) {
			return new NavigableKeySet(sortedMap().tailMap(fromElement, inclusive));
		}
	}

	/**
	 * Removes all values for the provided key. Unlike {@link #removeAll}, it
	 * returns the number of removed mappings.
	 */
	private int removeValuesForKey(Object key) {
		Collection<V> collection = Maps.safeRemove(map, key);

		int count = 0;
		if (collection != null) {
			count = collection.size();
			collection.clear();
			totalSize -= count;
		}
		return count;
	}

	private abstract class Itr<T> implements Iterator<T> {
		final Iterator<Map.Entry<K, Collection<V>>> keyIterator;
		K key;
		Collection<V> collection;
		Iterator<V> valueIterator;

		Itr() {
			keyIterator = map.entrySet().iterator();
			key = null;
			collection = null;
			valueIterator = Iterators.emptyModifiableIterator();
		}

		abstract T output(K key, V value);

		@Override
		public boolean hasNext() {
			return keyIterator.hasNext() || valueIterator.hasNext();
		}

		@Override
		public T next() {
			if (!valueIterator.hasNext()) {
				Map.Entry<K, Collection<V>> mapEntry = keyIterator.next();
				key = mapEntry.getKey();
				collection = mapEntry.getValue();
				valueIterator = collection.iterator();
			}
			return output(key, valueIterator.next());
		}

		@Override
		public void remove() {
			valueIterator.remove();
			if (collection.isEmpty()) {
				keyIterator.remove();
			}
			totalSize--;
		}
	}

	/**
	 * {@inheritDoc}
	 *
	 * <p>
	 * The iterator generated by the returned collection traverses the values for
	 * one key, followed by the values of a second key, and so on.
	 */
	@Override
	public Collection<V> values() {
		return super.values();
	}

	@Override
	Iterator<V> valueIterator() {
		return new Itr<V>() {
			@Override
			V output(K key, V value) {
				return value;
			}
		};
	}

	/*
	 * TODO(kevinb): should we copy this javadoc to each concrete class, so that
	 * classes like LinkedHashMultimap that need to say something different are
	 * still able to {@inheritDoc} all the way from Multimap?
	 */

	/**
	 * {@inheritDoc}
	 *
	 * <p>
	 * The iterator generated by the returned collection traverses the values for
	 * one key, followed by the values of a second key, and so on.
	 *
	 * <p>
	 * Each entry is an immutable snapshot of a key-value mapping in the multimap,
	 * taken at the time the entry is returned by a method call to the collection or
	 * its iterator.
	 */
	@Override
	public Collection<Map.Entry<K, V>> entries() {
		return super.entries();
	}

	/**
	 * Returns an iterator across all key-value map entries, used by {@code
	 * entries().iterator()} and {@code values().iterator()}. The default behavior,
	 * which traverses the values for one key, the values for a second key, and so
	 * on, suffices for most {@code AbstractMapBasedMultimap} implementations.
	 *
	 * @return an iterator across map entries
	 */
	@Override
	Iterator<Map.Entry<K, V>> entryIterator() {
		return new Itr<Map.Entry<K, V>>() {
			@Override
			Entry<K, V> output(K key, V value) {
				return Maps.immutableEntry(key, value);
			}
		};
	}

	@Override
	Map<K, Collection<V>> createAsMap() {
		// TreeMultimap uses NavigableAsMap explicitly, but we don't handle that here
		// for GWT
		// compatibility reasons
		return (map instanceof SortedMap) ? new SortedAsMap((SortedMap<K, Collection<V>>) map) : new AsMap(map);
	}

	private class AsMap extends ImprovedAbstractMap<K, Collection<V>> {
		/**
		 * Usually the same as map, but smaller for the headMap(), tailMap(), or
		 * subMap() of a SortedAsMap.
		 */
		final transient Map<K, Collection<V>> submap;

		AsMap(Map<K, Collection<V>> submap) {
			this.submap = submap;
		}

		@Override
		protected Set<Entry<K, Collection<V>>> createEntrySet() {
			return new AsMapEntries();
		}

		// The following methods are included for performance.

		@Override
		public boolean containsKey(Object key) {
			return Maps.safeContainsKey(submap, key);
		}

		@Override
		public Collection<V> get(Object key) {
			Collection<V> collection = Maps.safeGet(submap, key);
			if (collection == null) {
				return null;
			}
			@SuppressWarnings("unchecked")
			K k = (K) key;
			return wrapCollection(k, collection);
		}

		@Override
		public Set<K> keySet() {
			return AbstractMapBasedMultimap.this.keySet();
		}

		@Override
		public int size() {
			return submap.size();
		}

		@Override
		public Collection<V> remove(Object key) {
			Collection<V> collection = submap.remove(key);
			if (collection == null) {
				return null;
			}

			Collection<V> output = createCollection();
			output.addAll(collection);
			totalSize -= collection.size();
			collection.clear();
			return output;
		}

		@Override
		public boolean equals(@Nullable Object object) {
			return this == object || submap.equals(object);
		}

		@Override
		public int hashCode() {
			return submap.hashCode();
		}

		@Override
		public String toString() {
			return submap.toString();
		}

		@Override
		public void clear() {
			if (submap == map) {
				AbstractMapBasedMultimap.this.clear();
			} else {
				Iterators.clear(new AsMapIterator());
			}
		}

		Entry<K, Collection<V>> wrapEntry(Entry<K, Collection<V>> entry) {
			K key = entry.getKey();
			return Maps.immutableEntry(key, wrapCollection(key, entry.getValue()));
		}

		class AsMapEntries extends Maps.EntrySet<K, Collection<V>> {
			@Override
			Map<K, Collection<V>> map() {
				return AsMap.this;
			}

			@Override
			public Iterator<Map.Entry<K, Collection<V>>> iterator() {
				return new AsMapIterator();
			}

			// The following methods are included for performance.

			@Override
			public boolean contains(Object o) {
				return Collections2.safeContains(submap.entrySet(), o);
			}

			@Override
			public boolean remove(Object o) {
				if (!contains(o)) {
					return false;
				}
				Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
				removeValuesForKey(entry.getKey());
				return true;
			}
		}

		/** Iterator across all keys and value collections. */
		class AsMapIterator implements Iterator<Map.Entry<K, Collection<V>>> {
			final Iterator<Map.Entry<K, Collection<V>>> delegateIterator = submap.entrySet().iterator();
			Collection<V> collection;

			@Override
			public boolean hasNext() {
				return delegateIterator.hasNext();
			}

			@Override
			public Map.Entry<K, Collection<V>> next() {
				Map.Entry<K, Collection<V>> entry = delegateIterator.next();
				collection = entry.getValue();
				return wrapEntry(entry);
			}

			@Override
			public void remove() {
				delegateIterator.remove();
				totalSize -= collection.size();
				collection.clear();
			}
		}
	}

	private class SortedAsMap extends AsMap implements SortedMap<K, Collection<V>> {
		SortedAsMap(SortedMap<K, Collection<V>> submap) {
			super(submap);
		}

		SortedMap<K, Collection<V>> sortedMap() {
			return (SortedMap<K, Collection<V>>) submap;
		}

		@Override
		public Comparator<? super K> comparator() {
			return sortedMap().comparator();
		}

		@Override
		public K firstKey() {
			return sortedMap().firstKey();
		}

		@Override
		public K lastKey() {
			return sortedMap().lastKey();
		}

		@Override
		public SortedMap<K, Collection<V>> headMap(K toKey) {
			return new SortedAsMap(sortedMap().headMap(toKey));
		}

		@Override
		public SortedMap<K, Collection<V>> subMap(K fromKey, K toKey) {
			return new SortedAsMap(sortedMap().subMap(fromKey, toKey));
		}

		@Override
		public SortedMap<K, Collection<V>> tailMap(K fromKey) {
			return new SortedAsMap(sortedMap().tailMap(fromKey));
		}

		SortedSet<K> sortedKeySet;

		// returns a SortedSet, even though returning a Set would be sufficient to
		// satisfy the SortedMap.keySet() interface
		@Override
		public SortedSet<K> keySet() {
			SortedSet<K> result = sortedKeySet;
			return (result == null) ? sortedKeySet = createKeySet() : result;
		}

		@Override
		SortedSet<K> createKeySet() {
			return new SortedKeySet(sortedMap());
		}
	}

	@GwtIncompatible("NavigableAsMap")
	class NavigableAsMap extends SortedAsMap implements NavigableMap<K, Collection<V>> {

		NavigableAsMap(NavigableMap<K, Collection<V>> submap) {
			super(submap);
		}

		@Override
		NavigableMap<K, Collection<V>> sortedMap() {
			return (NavigableMap<K, Collection<V>>) super.sortedMap();
		}

		@Override
		public Entry<K, Collection<V>> lowerEntry(K key) {
			Entry<K, Collection<V>> entry = sortedMap().lowerEntry(key);
			return (entry == null) ? null : wrapEntry(entry);
		}

		@Override
		public K lowerKey(K key) {
			return sortedMap().lowerKey(key);
		}

		@Override
		public Entry<K, Collection<V>> floorEntry(K key) {
			Entry<K, Collection<V>> entry = sortedMap().floorEntry(key);
			return (entry == null) ? null : wrapEntry(entry);
		}

		@Override
		public K floorKey(K key) {
			return sortedMap().floorKey(key);
		}

		@Override
		public Entry<K, Collection<V>> ceilingEntry(K key) {
			Entry<K, Collection<V>> entry = sortedMap().ceilingEntry(key);
			return (entry == null) ? null : wrapEntry(entry);
		}

		@Override
		public K ceilingKey(K key) {
			return sortedMap().ceilingKey(key);
		}

		@Override
		public Entry<K, Collection<V>> higherEntry(K key) {
			Entry<K, Collection<V>> entry = sortedMap().higherEntry(key);
			return (entry == null) ? null : wrapEntry(entry);
		}

		@Override
		public K higherKey(K key) {
			return sortedMap().higherKey(key);
		}

		@Override
		public Entry<K, Collection<V>> firstEntry() {
			Entry<K, Collection<V>> entry = sortedMap().firstEntry();
			return (entry == null) ? null : wrapEntry(entry);
		}

		@Override
		public Entry<K, Collection<V>> lastEntry() {
			Entry<K, Collection<V>> entry = sortedMap().lastEntry();
			return (entry == null) ? null : wrapEntry(entry);
		}

		@Override
		public Entry<K, Collection<V>> pollFirstEntry() {
			return pollAsMapEntry(entrySet().iterator());
		}

		@Override
		public Entry<K, Collection<V>> pollLastEntry() {
			return pollAsMapEntry(descendingMap().entrySet().iterator());
		}

		Map.Entry<K, Collection<V>> pollAsMapEntry(Iterator<Entry<K, Collection<V>>> entryIterator) {
			if (!entryIterator.hasNext()) {
				return null;
			}
			Entry<K, Collection<V>> entry = entryIterator.next();
			Collection<V> output = createCollection();
			output.addAll(entry.getValue());
			entryIterator.remove();
			return Maps.immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output));
		}

		@Override
		public NavigableMap<K, Collection<V>> descendingMap() {
			return new NavigableAsMap(sortedMap().descendingMap());
		}

		@Override
		public NavigableSet<K> keySet() {
			return (NavigableSet<K>) super.keySet();
		}

		@Override
		NavigableSet<K> createKeySet() {
			return new NavigableKeySet(sortedMap());
		}

		@Override
		public NavigableSet<K> navigableKeySet() {
			return keySet();
		}

		@Override
		public NavigableSet<K> descendingKeySet() {
			return descendingMap().navigableKeySet();
		}

		@Override
		public NavigableMap<K, Collection<V>> subMap(K fromKey, K toKey) {
			return subMap(fromKey, true, toKey, false);
		}

		@Override
		public NavigableMap<K, Collection<V>> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
			return new NavigableAsMap(sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive));
		}

		@Override
		public NavigableMap<K, Collection<V>> headMap(K toKey) {
			return headMap(toKey, false);
		}

		@Override
		public NavigableMap<K, Collection<V>> headMap(K toKey, boolean inclusive) {
			return new NavigableAsMap(sortedMap().headMap(toKey, inclusive));
		}

		@Override
		public NavigableMap<K, Collection<V>> tailMap(K fromKey) {
			return tailMap(fromKey, true);
		}

		@Override
		public NavigableMap<K, Collection<V>> tailMap(K fromKey, boolean inclusive) {
			return new NavigableAsMap(sortedMap().tailMap(fromKey, inclusive));
		}
	}

	private static final long serialVersionUID = 2447537837011683357L;
}
