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

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.j2objc.annotations.Weak;
import com.google.j2objc.annotations.WeakOuter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.BiConsumer;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

A Multimap whose contents will never change, with many other important properties detailed at ImmutableCollection.

Warning: avoid direct usage of ImmutableMultimap as a type (as with Multimap itself). Prefer subtypes such as ImmutableSetMultimap or ImmutableListMultimap, which have well-defined AbstractMultimap.equals semantics, thus avoiding a common source of bugs and confusion.

Note: every ImmutableMultimap offers an inverse view, so there is no need for a distinct ImmutableBiMultimap type.

Key-grouped iteration. All view collections follow the same iteration order. In all current implementations, the iteration order always keeps multiple entries with the same key together. Any creation method that would customarily respect insertion order (such as copyOf(Multimap<? extends Object,? extends Object>)) instead preserves key-grouped order by inserting entries for an existing key immediately after the last entry having that key.

See the Guava User Guide article on immutable collections.

Author:Jared Levy
Since:2.0
/** * A {@link Multimap} whose contents will never change, with many other important properties * detailed at {@link ImmutableCollection}. * * <p><b>Warning:</b> avoid <i>direct</i> usage of {@link ImmutableMultimap} as a type (as with * {@link Multimap} itself). Prefer subtypes such as {@link ImmutableSetMultimap} or {@link * ImmutableListMultimap}, which have well-defined {@link #equals} semantics, thus avoiding a common * source of bugs and confusion. * * <p><b>Note:</b> every {@link ImmutableMultimap} offers an {@link #inverse} view, so there is no * need for a distinct {@code ImmutableBiMultimap} type. * * <p><a name="iteration"></a> * * <p><b>Key-grouped iteration.</b> All view collections follow the same iteration order. In all * current implementations, the iteration order always keeps multiple entries with the same key * together. Any creation method that would customarily respect insertion order (such as {@link * #copyOf(Multimap)}) instead preserves key-grouped order by inserting entries for an existing key * immediately after the last entry having that key. * * <p>See the Guava User Guide article on <a href= * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained"> immutable collections</a>. * * @author Jared Levy * @since 2.0 */
@GwtCompatible(emulated = true) public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> implements Serializable {
Returns an empty multimap.
/** Returns an empty multimap. */
public static <K, V> ImmutableMultimap<K, V> of() { return ImmutableListMultimap.of(); }
Returns an immutable multimap containing a single entry.
/** Returns an immutable multimap containing a single entry. */
public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1) { return ImmutableListMultimap.of(k1, v1); }
Returns an immutable multimap containing the given entries, in order.
/** Returns an immutable multimap containing the given entries, in order. */
public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1, K k2, V v2) { return ImmutableListMultimap.of(k1, v1, k2, v2); }
Returns an immutable multimap containing the given entries, in the "key-grouped" insertion order described in the class documentation.
/** * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion * order described in the <a href="#iteration">class documentation</a>. */
public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) { return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3); }
Returns an immutable multimap containing the given entries, in the "key-grouped" insertion order described in the class documentation.
/** * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion * order described in the <a href="#iteration">class documentation</a>. */
public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4); }
Returns an immutable multimap containing the given entries, in the "key-grouped" insertion order described in the class documentation.
/** * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion * order described in the <a href="#iteration">class documentation</a>. */
public static <K, V> ImmutableMultimap<K, V> of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); } // looking for of() with > 5 entries? Use the builder instead.
Returns a new builder. The generated builder is equivalent to the builder created by the Builder constructor.
/** * Returns a new builder. The generated builder is equivalent to the builder created by the {@link * Builder} constructor. */
public static <K, V> Builder<K, V> builder() { return new Builder<>(); }
A builder for creating immutable multimap instances, especially public static final multimaps ("constant multimaps"). Example:

static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP =
    new ImmutableMultimap.Builder<String, Integer>()
        .put("one", 1)
        .putAll("several", 1, 2, 3)
        .putAll("many", 1, 2, 3, 4, 5)
        .build();

Builder instances can be reused; it is safe to call build multiple times to build multiple multimaps in series. Each multimap contains the key-value mappings in the previously created multimaps.

Since:2.0
/** * A builder for creating immutable multimap instances, especially {@code public static final} * multimaps ("constant multimaps"). Example: * * <pre>{@code * static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP = * new ImmutableMultimap.Builder<String, Integer>() * .put("one", 1) * .putAll("several", 1, 2, 3) * .putAll("many", 1, 2, 3, 4, 5) * .build(); * }</pre> * * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build * multiple multimaps in series. Each multimap contains the key-value mappings in the previously * created multimaps. * * @since 2.0 */
public static class Builder<K, V> { Map<K, Collection<V>> builderMap; @MonotonicNonNull Comparator<? super K> keyComparator; @MonotonicNonNull Comparator<? super V> valueComparator;
Creates a new builder. The returned builder is equivalent to the builder generated by ImmutableMultimap.builder.
/** * Creates a new builder. The returned builder is equivalent to the builder generated by {@link * ImmutableMultimap#builder}. */
public Builder() { this.builderMap = Platform.preservesInsertionOrderOnPutsMap(); } Collection<V> newMutableValueCollection() { return new ArrayList<>(); }
Adds a key-value mapping to the built multimap.
/** Adds a key-value mapping to the built multimap. */
@CanIgnoreReturnValue public Builder<K, V> put(K key, V value) { checkEntryNotNull(key, value); Collection<V> valueCollection = builderMap.get(key); if (valueCollection == null) { builderMap.put(key, valueCollection = newMutableValueCollection()); } valueCollection.add(value); return this; }
Adds an entry to the built multimap.
Since:11.0
/** * Adds an entry to the built multimap. * * @since 11.0 */
@CanIgnoreReturnValue public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { return put(entry.getKey(), entry.getValue()); }
Adds entries to the built multimap.
Since:19.0
/** * Adds entries to the built multimap. * * @since 19.0 */
@CanIgnoreReturnValue @Beta public Builder<K, V> putAll(Iterable<? extends Entry<? extends K, ? extends V>> entries) { for (Entry<? extends K, ? extends V> entry : entries) { put(entry); } return this; }
Stores a collection of values with the same key in the built multimap.
Throws:
  • NullPointerException – if key, values, or any element in values is null. The builder is left in an invalid state.
/** * Stores a collection of values with the same key in the built multimap. * * @throws NullPointerException if {@code key}, {@code values}, or any element in {@code values} * is null. The builder is left in an invalid state. */
@CanIgnoreReturnValue public Builder<K, V> putAll(K key, Iterable<? extends V> values) { if (key == null) { throw new NullPointerException("null key in entry: null=" + Iterables.toString(values)); } Collection<V> valueCollection = builderMap.get(key); if (valueCollection != null) { for (V value : values) { checkEntryNotNull(key, value); valueCollection.add(value); } return this; } Iterator<? extends V> valuesItr = values.iterator(); if (!valuesItr.hasNext()) { return this; } valueCollection = newMutableValueCollection(); while (valuesItr.hasNext()) { V value = valuesItr.next(); checkEntryNotNull(key, value); valueCollection.add(value); } builderMap.put(key, valueCollection); return this; }
Stores an array of values with the same key in the built multimap.
Throws:
  • NullPointerException – if the key or any value is null. The builder is left in an invalid state.
/** * Stores an array of values with the same key in the built multimap. * * @throws NullPointerException if the key or any value is null. The builder is left in an * invalid state. */
@CanIgnoreReturnValue public Builder<K, V> putAll(K key, V... values) { return putAll(key, Arrays.asList(values)); }
Stores another multimap's entries in the built multimap. The generated multimap's key and value orderings correspond to the iteration ordering of the multimap.asMap() view, with new keys and values following any existing keys and values.
Throws:
  • NullPointerException – if any key or value in multimap is null. The builder is left in an invalid state.
/** * Stores another multimap's entries in the built multimap. The generated multimap's key and * value orderings correspond to the iteration ordering of the {@code multimap.asMap()} view, * with new keys and values following any existing keys and values. * * @throws NullPointerException if any key or value in {@code multimap} is null. The builder is * left in an invalid state. */
@CanIgnoreReturnValue public Builder<K, V> putAll(Multimap<? extends K, ? extends V> multimap) { for (Entry<? extends K, ? extends Collection<? extends V>> entry : multimap.asMap().entrySet()) { putAll(entry.getKey(), entry.getValue()); } return this; }
Specifies the ordering of the generated multimap's keys.
Since:8.0
/** * Specifies the ordering of the generated multimap's keys. * * @since 8.0 */
@CanIgnoreReturnValue public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { this.keyComparator = checkNotNull(keyComparator); return this; }
Specifies the ordering of the generated multimap's values for each key.
Since:8.0
/** * Specifies the ordering of the generated multimap's values for each key. * * @since 8.0 */
@CanIgnoreReturnValue public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { this.valueComparator = checkNotNull(valueComparator); return this; } @CanIgnoreReturnValue Builder<K, V> combine(Builder<K, V> other) { for (Map.Entry<K, Collection<V>> entry : other.builderMap.entrySet()) { putAll(entry.getKey(), entry.getValue()); } return this; }
Returns a newly-created immutable multimap.
/** Returns a newly-created immutable multimap. */
public ImmutableMultimap<K, V> build() { Collection<Map.Entry<K, Collection<V>>> mapEntries = builderMap.entrySet(); if (keyComparator != null) { mapEntries = Ordering.from(keyComparator).<K>onKeys().immutableSortedCopy(mapEntries); } return ImmutableListMultimap.fromMapEntries(mapEntries, valueComparator); } }
Returns an immutable multimap containing the same mappings as multimap, in the "key-grouped" iteration order described in the class documentation.

Despite the method name, this method attempts to avoid actually copying the data when it is safe to do so. The exact circumstances under which a copy will or will not be performed are undocumented and subject to change.

Throws:
/** * Returns an immutable multimap containing the same mappings as {@code multimap}, in the * "key-grouped" iteration order described in the class documentation. * * <p>Despite the method name, this method attempts to avoid actually copying the data when it is * safe to do so. The exact circumstances under which a copy will or will not be performed are * undocumented and subject to change. * * @throws NullPointerException if any key or value in {@code multimap} is null */
public static <K, V> ImmutableMultimap<K, V> copyOf(Multimap<? extends K, ? extends V> multimap) { if (multimap instanceof ImmutableMultimap) { @SuppressWarnings("unchecked") // safe since multimap is not writable ImmutableMultimap<K, V> kvMultimap = (ImmutableMultimap<K, V>) multimap; if (!kvMultimap.isPartialView()) { return kvMultimap; } } return ImmutableListMultimap.copyOf(multimap); }
Returns an immutable multimap containing the specified entries. The returned multimap iterates over keys in the order they were first encountered in the input, and the values for each key are iterated in the order they were encountered.
Throws:
  • NullPointerException – if any key, value, or entry is null
Since:19.0
/** * Returns an immutable multimap containing the specified entries. The returned multimap iterates * over keys in the order they were first encountered in the input, and the values for each key * are iterated in the order they were encountered. * * @throws NullPointerException if any key, value, or entry is null * @since 19.0 */
@Beta public static <K, V> ImmutableMultimap<K, V> copyOf( Iterable<? extends Entry<? extends K, ? extends V>> entries) { return ImmutableListMultimap.copyOf(entries); } final transient ImmutableMap<K, ? extends ImmutableCollection<V>> map; final transient int size; // These constants allow the deserialization code to set final fields. This // holder class makes sure they are not initialized unless an instance is // deserialized. @GwtIncompatible // java serialization is not supported static class FieldSettersHolder { static final Serialization.FieldSetter<ImmutableMultimap> MAP_FIELD_SETTER = Serialization.getFieldSetter(ImmutableMultimap.class, "map"); static final Serialization.FieldSetter<ImmutableMultimap> SIZE_FIELD_SETTER = Serialization.getFieldSetter(ImmutableMultimap.class, "size"); } ImmutableMultimap(ImmutableMap<K, ? extends ImmutableCollection<V>> map, int size) { this.map = map; this.size = size; } // mutators (not supported)
Guaranteed to throw an exception and leave the multimap unmodified.
Throws:
  • UnsupportedOperationException – always
Deprecated:Unsupported operation.
/** * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always * @deprecated Unsupported operation. */
@CanIgnoreReturnValue @Deprecated @Override public ImmutableCollection<V> removeAll(Object key) { throw new UnsupportedOperationException(); }
Guaranteed to throw an exception and leave the multimap unmodified.
Throws:
  • UnsupportedOperationException – always
Deprecated:Unsupported operation.
/** * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always * @deprecated Unsupported operation. */
@CanIgnoreReturnValue @Deprecated @Override public ImmutableCollection<V> replaceValues(K key, Iterable<? extends V> values) { throw new UnsupportedOperationException(); }
Guaranteed to throw an exception and leave the multimap unmodified.
Throws:
  • UnsupportedOperationException – always
Deprecated:Unsupported operation.
/** * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always * @deprecated Unsupported operation. */
@Deprecated @Override public void clear() { throw new UnsupportedOperationException(); }
Returns an immutable collection of the values for the given key. If no mappings in the multimap have the provided key, an empty immutable collection is returned. The values are in the same order as the parameters used to build this multimap.
/** * Returns an immutable collection of the values for the given key. If no mappings in the multimap * have the provided key, an empty immutable collection is returned. The values are in the same * order as the parameters used to build this multimap. */
@Override public abstract ImmutableCollection<V> get(K key);
Returns an immutable multimap which is the inverse of this one. For every key-value mapping in the original, the result will have a mapping with key and value reversed.
Since:11.0
/** * Returns an immutable multimap which is the inverse of this one. For every key-value mapping in * the original, the result will have a mapping with key and value reversed. * * @since 11.0 */
public abstract ImmutableMultimap<V, K> inverse();
Guaranteed to throw an exception and leave the multimap unmodified.
Throws:
  • UnsupportedOperationException – always
Deprecated:Unsupported operation.
/** * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always * @deprecated Unsupported operation. */
@CanIgnoreReturnValue @Deprecated @Override public boolean put(K key, V value) { throw new UnsupportedOperationException(); }
Guaranteed to throw an exception and leave the multimap unmodified.
Throws:
  • UnsupportedOperationException – always
Deprecated:Unsupported operation.
/** * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always * @deprecated Unsupported operation. */
@CanIgnoreReturnValue @Deprecated @Override public boolean putAll(K key, Iterable<? extends V> values) { throw new UnsupportedOperationException(); }
Guaranteed to throw an exception and leave the multimap unmodified.
Throws:
  • UnsupportedOperationException – always
Deprecated:Unsupported operation.
/** * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always * @deprecated Unsupported operation. */
@CanIgnoreReturnValue @Deprecated @Override public boolean putAll(Multimap<? extends K, ? extends V> multimap) { throw new UnsupportedOperationException(); }
Guaranteed to throw an exception and leave the multimap unmodified.
Throws:
  • UnsupportedOperationException – always
Deprecated:Unsupported operation.
/** * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always * @deprecated Unsupported operation. */
@CanIgnoreReturnValue @Deprecated @Override public boolean remove(Object key, Object value) { throw new UnsupportedOperationException(); }
Returns true if this immutable multimap's implementation contains references to user-created objects that aren't accessible via this multimap's methods. This is generally used to determine whether copyOf implementations should make an explicit copy to avoid memory leaks.
/** * Returns {@code true} if this immutable multimap's implementation contains references to * user-created objects that aren't accessible via this multimap's methods. This is generally used * to determine whether {@code copyOf} implementations should make an explicit copy to avoid * memory leaks. */
boolean isPartialView() { return map.isPartialView(); } // accessors @Override public boolean containsKey(@Nullable Object key) { return map.containsKey(key); } @Override public boolean containsValue(@Nullable Object value) { return value != null && super.containsValue(value); } @Override public int size() { return size; } // views
Returns an immutable set of the distinct keys in this multimap, in the same order as they appear in this multimap.
/** * Returns an immutable set of the distinct keys in this multimap, in the same order as they * appear in this multimap. */
@Override public ImmutableSet<K> keySet() { return map.keySet(); } @Override Set<K> createKeySet() { throw new AssertionError("unreachable"); }
Returns an immutable map that associates each key with its corresponding values in the multimap. Keys and values appear in the same order as in this multimap.
/** * Returns an immutable map that associates each key with its corresponding values in the * multimap. Keys and values appear in the same order as in this multimap. */
@Override @SuppressWarnings("unchecked") // a widening cast public ImmutableMap<K, Collection<V>> asMap() { return (ImmutableMap) map; } @Override Map<K, Collection<V>> createAsMap() { throw new AssertionError("should never be called"); }
Returns an immutable collection of all key-value pairs in the multimap.
/** Returns an immutable collection of all key-value pairs in the multimap. */
@Override public ImmutableCollection<Entry<K, V>> entries() { return (ImmutableCollection<Entry<K, V>>) super.entries(); } @Override ImmutableCollection<Entry<K, V>> createEntries() { return new EntryCollection<>(this); } private static class EntryCollection<K, V> extends ImmutableCollection<Entry<K, V>> { @Weak final ImmutableMultimap<K, V> multimap; EntryCollection(ImmutableMultimap<K, V> multimap) { this.multimap = multimap; } @Override public UnmodifiableIterator<Entry<K, V>> iterator() { return multimap.entryIterator(); } @Override boolean isPartialView() { return multimap.isPartialView(); } @Override public int size() { return multimap.size(); } @Override public boolean contains(Object object) { if (object instanceof Entry) { Entry<?, ?> entry = (Entry<?, ?>) object; return multimap.containsEntry(entry.getKey(), entry.getValue()); } return false; } private static final long serialVersionUID = 0; } @Override UnmodifiableIterator<Entry<K, V>> entryIterator() { return new UnmodifiableIterator<Entry<K, V>>() { final Iterator<? extends Entry<K, ? extends ImmutableCollection<V>>> asMapItr = map.entrySet().iterator(); K currentKey = null; Iterator<V> valueItr = Iterators.emptyIterator(); @Override public boolean hasNext() { return valueItr.hasNext() || asMapItr.hasNext(); } @Override public Entry<K, V> next() { if (!valueItr.hasNext()) { Entry<K, ? extends ImmutableCollection<V>> entry = asMapItr.next(); currentKey = entry.getKey(); valueItr = entry.getValue().iterator(); } return Maps.immutableEntry(currentKey, valueItr.next()); } }; } @Override Spliterator<Entry<K, V>> entrySpliterator() { return CollectSpliterators.flatMap( asMap().entrySet().spliterator(), keyToValueCollectionEntry -> { K key = keyToValueCollectionEntry.getKey(); Collection<V> valueCollection = keyToValueCollectionEntry.getValue(); return CollectSpliterators.map( valueCollection.spliterator(), (V value) -> Maps.immutableEntry(key, value)); }, Spliterator.SIZED | (this instanceof SetMultimap ? Spliterator.DISTINCT : 0), size()); } @Override public void forEach(BiConsumer<? super K, ? super V> action) { checkNotNull(action); asMap() .forEach( (key, valueCollection) -> valueCollection.forEach(value -> action.accept(key, value))); }
Returns an immutable multiset containing all the keys in this multimap, in the same order and with the same frequencies as they appear in this multimap; to get only a single occurrence of each key, use keySet.
/** * Returns an immutable multiset containing all the keys in this multimap, in the same order and * with the same frequencies as they appear in this multimap; to get only a single occurrence of * each key, use {@link #keySet}. */
@Override public ImmutableMultiset<K> keys() { return (ImmutableMultiset<K>) super.keys(); } @Override ImmutableMultiset<K> createKeys() { return new Keys(); } @SuppressWarnings("serial") // Uses writeReplace, not default serialization @WeakOuter class Keys extends ImmutableMultiset<K> { @Override public boolean contains(@Nullable Object object) { return containsKey(object); } @Override public int count(@Nullable Object element) { Collection<V> values = map.get(element); return (values == null) ? 0 : values.size(); } @Override public ImmutableSet<K> elementSet() { return keySet(); } @Override public int size() { return ImmutableMultimap.this.size(); } @Override Multiset.Entry<K> getEntry(int index) { Map.Entry<K, ? extends Collection<V>> entry = map.entrySet().asList().get(index); return Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); } @Override boolean isPartialView() { return true; } @GwtIncompatible @Override Object writeReplace() { return new KeysSerializedForm(ImmutableMultimap.this); } } @GwtIncompatible private static final class KeysSerializedForm implements Serializable { final ImmutableMultimap<?, ?> multimap; KeysSerializedForm(ImmutableMultimap<?, ?> multimap) { this.multimap = multimap; } Object readResolve() { return multimap.keys(); } }
Returns an immutable collection of the values in this multimap. Its iterator traverses the values for the first key, the values for the second key, and so on.
/** * Returns an immutable collection of the values in this multimap. Its iterator traverses the * values for the first key, the values for the second key, and so on. */
@Override public ImmutableCollection<V> values() { return (ImmutableCollection<V>) super.values(); } @Override ImmutableCollection<V> createValues() { return new Values<>(this); } @Override UnmodifiableIterator<V> valueIterator() { return new UnmodifiableIterator<V>() { Iterator<? extends ImmutableCollection<V>> valueCollectionItr = map.values().iterator(); Iterator<V> valueItr = Iterators.emptyIterator(); @Override public boolean hasNext() { return valueItr.hasNext() || valueCollectionItr.hasNext(); } @Override public V next() { if (!valueItr.hasNext()) { valueItr = valueCollectionItr.next().iterator(); } return valueItr.next(); } }; } private static final class Values<K, V> extends ImmutableCollection<V> { @Weak private final transient ImmutableMultimap<K, V> multimap; Values(ImmutableMultimap<K, V> multimap) { this.multimap = multimap; } @Override public boolean contains(@Nullable Object object) { return multimap.containsValue(object); } @Override public UnmodifiableIterator<V> iterator() { return multimap.valueIterator(); } @GwtIncompatible // not present in emulated superclass @Override int copyIntoArray(Object[] dst, int offset) { for (ImmutableCollection<V> valueCollection : multimap.map.values()) { offset = valueCollection.copyIntoArray(dst, offset); } return offset; } @Override public int size() { return multimap.size(); } @Override boolean isPartialView() { return true; } private static final long serialVersionUID = 0; } private static final long serialVersionUID = 0; }