/*
* 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.checkNotNull;
import static com.google.common.collect.CollectPreconditions.checkNonnegative;
import static com.google.common.collect.CollectPreconditions.checkRemove;
import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.Maps.EntryTransformer;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.j2objc.annotations.Weak;
import com.google.j2objc.annotations.WeakOuter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Collector;
import java.util.stream.Stream;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
Provides static methods acting on or generating a Multimap
. See the Guava User Guide article on
Multimaps
.
Author: Jared Levy, Robert Konigsberg, Mike Bostock, Louis Wasserman Since: 2.0
/**
* Provides static methods acting on or generating a {@code Multimap}.
*
* <p>See the Guava User Guide article on <a href=
* "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#multimaps"> {@code
* Multimaps}</a>.
*
* @author Jared Levy
* @author Robert Konigsberg
* @author Mike Bostock
* @author Louis Wasserman
* @since 2.0
*/
@GwtCompatible(emulated = true)
public final class Multimaps {
private Multimaps() {}
Returns a Collector
accumulating entries into a Multimap
generated from the specified supplier. The keys and values of the entries are the result of applying the provided mapping functions to the input elements, accumulated in the encounter order of the stream. Example:
static final ListMultimap<Character, String> FIRST_LETTER_MULTIMAP =
Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
.collect(
toMultimap(
str -> str.charAt(0),
str -> str.substring(1),
MultimapBuilder.treeKeys().arrayListValues()::build));
// is equivalent to
static final ListMultimap<Character, String> FIRST_LETTER_MULTIMAP;
static {
FIRST_LETTER_MULTIMAP = MultimapBuilder.treeKeys().arrayListValues().build();
FIRST_LETTER_MULTIMAP.put('b', "anana");
FIRST_LETTER_MULTIMAP.put('a', "pple");
FIRST_LETTER_MULTIMAP.put('a', "sparagus");
FIRST_LETTER_MULTIMAP.put('c', "arrot");
FIRST_LETTER_MULTIMAP.put('c', "herry");
}
Since: 21.0
/**
* Returns a {@code Collector} accumulating entries into a {@code Multimap} generated from the
* specified supplier. The keys and values of the entries are the result of applying the provided
* mapping functions to the input elements, accumulated in the encounter order of the stream.
*
* <p>Example:
*
* <pre>{@code
* static final ListMultimap<Character, String> FIRST_LETTER_MULTIMAP =
* Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
* .collect(
* toMultimap(
* str -> str.charAt(0),
* str -> str.substring(1),
* MultimapBuilder.treeKeys().arrayListValues()::build));
*
* // is equivalent to
*
* static final ListMultimap<Character, String> FIRST_LETTER_MULTIMAP;
*
* static {
* FIRST_LETTER_MULTIMAP = MultimapBuilder.treeKeys().arrayListValues().build();
* FIRST_LETTER_MULTIMAP.put('b', "anana");
* FIRST_LETTER_MULTIMAP.put('a', "pple");
* FIRST_LETTER_MULTIMAP.put('a', "sparagus");
* FIRST_LETTER_MULTIMAP.put('c', "arrot");
* FIRST_LETTER_MULTIMAP.put('c', "herry");
* }
* }</pre>
*
* @since 21.0
*/
@Beta
public static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> toMultimap(
java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends V> valueFunction,
java.util.function.Supplier<M> multimapSupplier) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(multimapSupplier);
return Collector.of(
multimapSupplier,
(multimap, input) -> multimap.put(keyFunction.apply(input), valueFunction.apply(input)),
(multimap1, multimap2) -> {
multimap1.putAll(multimap2);
return multimap1;
});
}
Returns a Collector
accumulating entries into a Multimap
generated from the specified supplier. Each input element is mapped to a key and a stream of values, each of which are put into the resulting Multimap
, in the encounter order of the stream and the encounter order of the streams of values. Example:
static final ListMultimap<Character, Character> FIRST_LETTER_MULTIMAP =
Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
.collect(
flatteningToMultimap(
str -> str.charAt(0),
str -> str.substring(1).chars().mapToObj(c -> (char) c),
MultimapBuilder.linkedHashKeys().arrayListValues()::build));
// is equivalent to
static final ListMultimap<Character, Character> FIRST_LETTER_MULTIMAP;
static {
FIRST_LETTER_MULTIMAP = MultimapBuilder.linkedHashKeys().arrayListValues().build();
FIRST_LETTER_MULTIMAP.putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'));
FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('p', 'p', 'l', 'e'));
FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'));
FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'));
FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'));
}
Since: 21.0
/**
* Returns a {@code Collector} accumulating entries into a {@code Multimap} generated from the
* specified supplier. Each input element is mapped to a key and a stream of values, each of which
* are put into the resulting {@code Multimap}, in the encounter order of the stream and the
* encounter order of the streams of values.
*
* <p>Example:
*
* <pre>{@code
* static final ListMultimap<Character, Character> FIRST_LETTER_MULTIMAP =
* Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
* .collect(
* flatteningToMultimap(
* str -> str.charAt(0),
* str -> str.substring(1).chars().mapToObj(c -> (char) c),
* MultimapBuilder.linkedHashKeys().arrayListValues()::build));
*
* // is equivalent to
*
* static final ListMultimap<Character, Character> FIRST_LETTER_MULTIMAP;
*
* static {
* FIRST_LETTER_MULTIMAP = MultimapBuilder.linkedHashKeys().arrayListValues().build();
* FIRST_LETTER_MULTIMAP.putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'));
* FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('p', 'p', 'l', 'e'));
* FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'));
* FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'));
* FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'));
* }
* }</pre>
*
* @since 21.0
*/
@Beta
public static <T, K, V, M extends Multimap<K, V>> Collector<T, ?, M> flatteningToMultimap(
java.util.function.Function<? super T, ? extends K> keyFunction,
java.util.function.Function<? super T, ? extends Stream<? extends V>> valueFunction,
java.util.function.Supplier<M> multimapSupplier) {
checkNotNull(keyFunction);
checkNotNull(valueFunction);
checkNotNull(multimapSupplier);
return Collector.of(
multimapSupplier,
(multimap, input) -> {
K key = keyFunction.apply(input);
Collection<V> valuesForKey = multimap.get(key);
valueFunction.apply(input).forEachOrdered(valuesForKey::add);
},
(multimap1, multimap2) -> {
multimap1.putAll(multimap2);
return multimap1;
});
}
Creates a new Multimap
backed by map
, whose internal value collections are generated by factory
. Warning: do not use this method when the collections returned by factory
implement either List
or Set
! Use the more specific method newListMultimap
, newSetMultimap
or newSortedSetMultimap
instead, to avoid very surprising behavior from Multimap.equals
.
The factory
-generated and map
classes determine the multimap iteration order. They also specify the behavior of the equals
, hashCode
, and
toString
methods for the multimap and its returned views. However, the multimap's get
method returns instances of a different class than factory.get()
does.
The multimap is serializable if map
, factory
, the collections generated by factory
, and the multimap contents are all serializable.
The multimap is not threadsafe when any concurrent operations update the multimap, even if map
and the instances generated by factory
are. Concurrent read operations will work correctly. To allow concurrent update operations, wrap the multimap with a call to synchronizedMultimap
.
Call this method only when the simpler methods ArrayListMultimap.create()
, HashMultimap.create()
, LinkedHashMultimap.create()
, LinkedListMultimap.create()
, TreeMultimap.create()
, and TreeMultimap.create(Comparator<? super Object>, Comparator<? super Object>)
won't suffice.
Note: the multimap assumes complete ownership over of map
and the collections returned by factory
. Those objects should not be manually updated and they should not use soft, weak, or phantom references.
Params: - map – place to store the mapping from each key to its corresponding values
- factory – supplier of new, empty collections that will each hold all values for a given
key
Throws: - IllegalArgumentException – if
map
is not empty
/**
* Creates a new {@code Multimap} backed by {@code map}, whose internal value collections are
* generated by {@code factory}.
*
* <p><b>Warning: do not use</b> this method when the collections returned by {@code factory}
* implement either {@link List} or {@code Set}! Use the more specific method {@link
* #newListMultimap}, {@link #newSetMultimap} or {@link #newSortedSetMultimap} instead, to avoid
* very surprising behavior from {@link Multimap#equals}.
*
* <p>The {@code factory}-generated and {@code map} classes determine the multimap iteration
* order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code
* toString} methods for the multimap and its returned views. However, the multimap's {@code get}
* method returns instances of a different class than {@code factory.get()} does.
*
* <p>The multimap is serializable if {@code map}, {@code factory}, the collections generated by
* {@code factory}, and the multimap contents are all serializable.
*
* <p>The multimap is not threadsafe when any concurrent operations update the multimap, even if
* {@code map} and the instances generated by {@code factory} are. Concurrent read operations will
* work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link
* #synchronizedMultimap}.
*
* <p>Call this method only when the simpler methods {@link ArrayListMultimap#create()}, {@link
* HashMultimap#create()}, {@link LinkedHashMultimap#create()}, {@link
* LinkedListMultimap#create()}, {@link TreeMultimap#create()}, and {@link
* TreeMultimap#create(Comparator, Comparator)} won't suffice.
*
* <p>Note: the multimap assumes complete ownership over of {@code map} and the collections
* returned by {@code factory}. Those objects should not be manually updated and they should not
* use soft, weak, or phantom references.
*
* @param map place to store the mapping from each key to its corresponding values
* @param factory supplier of new, empty collections that will each hold all values for a given
* key
* @throws IllegalArgumentException if {@code map} is not empty
*/
public static <K, V> Multimap<K, V> newMultimap(
Map<K, Collection<V>> map, final Supplier<? extends Collection<V>> factory) {
return new CustomMultimap<>(map, factory);
}
private static class CustomMultimap<K, V> extends AbstractMapBasedMultimap<K, V> {
transient Supplier<? extends Collection<V>> factory;
CustomMultimap(Map<K, Collection<V>> map, Supplier<? extends Collection<V>> factory) {
super(map);
this.factory = checkNotNull(factory);
}
@Override
Set<K> createKeySet() {
return createMaybeNavigableKeySet();
}
@Override
Map<K, Collection<V>> createAsMap() {
return createMaybeNavigableAsMap();
}
@Override
protected Collection<V> createCollection() {
return factory.get();
}
@Override
<E> Collection<E> unmodifiableCollectionSubclass(Collection<E> collection) {
if (collection instanceof NavigableSet) {
return Sets.unmodifiableNavigableSet((NavigableSet<E>) collection);
} else if (collection instanceof SortedSet) {
return Collections.unmodifiableSortedSet((SortedSet<E>) collection);
} else if (collection instanceof Set) {
return Collections.unmodifiableSet((Set<E>) collection);
} else if (collection instanceof List) {
return Collections.unmodifiableList((List<E>) collection);
} else {
return Collections.unmodifiableCollection(collection);
}
}
@Override
Collection<V> wrapCollection(K key, Collection<V> collection) {
if (collection instanceof List) {
return wrapList(key, (List<V>) collection, null);
} else if (collection instanceof NavigableSet) {
return new WrappedNavigableSet(key, (NavigableSet<V>) collection, null);
} else 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 {
return new WrappedCollection(key, collection, null);
}
}
// can't use Serialization writeMultimap and populateMultimap methods since
// there's no way to generate the empty backing map.
@serialData the factory and the backing map
/** @serialData the factory and the backing map */
@GwtIncompatible // java.io.ObjectOutputStream
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeObject(factory);
stream.writeObject(backingMap());
}
@GwtIncompatible // java.io.ObjectInputStream
@SuppressWarnings("unchecked") // reading data stored by writeObject
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
factory = (Supplier<? extends Collection<V>>) stream.readObject();
Map<K, Collection<V>> map = (Map<K, Collection<V>>) stream.readObject();
setMap(map);
}
@GwtIncompatible // java serialization not supported
private static final long serialVersionUID = 0;
}
Creates a new ListMultimap
that uses the provided map and factory. It can generate a multimap based on arbitrary Map
and List
classes. The factory
-generated and map
classes determine the multimap iteration order. They also specify the behavior of the equals
, hashCode
, and
toString
methods for the multimap and its returned views. The multimap's get
,
removeAll
, and replaceValues
methods return RandomAccess
lists if the factory does. However, the multimap's get
method returns instances of a different class than does factory.get()
.
The multimap is serializable if map
, factory
, the lists generated by
factory
, and the multimap contents are all serializable.
The multimap is not threadsafe when any concurrent operations update the multimap, even if map
and the instances generated by factory
are. Concurrent read operations will work correctly. To allow concurrent update operations, wrap the multimap with a call to synchronizedListMultimap
.
Call this method only when the simpler methods ArrayListMultimap.create()
and LinkedListMultimap.create()
won't suffice.
Note: the multimap assumes complete ownership over of map
and the lists returned by factory
. Those objects should not be manually updated, they should be empty when provided, and they should not use soft, weak, or phantom references.
Params: - map – place to store the mapping from each key to its corresponding values
- factory – supplier of new, empty lists that will each hold all values for a given key
Throws: - IllegalArgumentException – if
map
is not empty
/**
* Creates a new {@code ListMultimap} that uses the provided map and factory. It can generate a
* multimap based on arbitrary {@link Map} and {@link List} classes.
*
* <p>The {@code factory}-generated and {@code map} classes determine the multimap iteration
* order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code
* toString} methods for the multimap and its returned views. The multimap's {@code get}, {@code
* removeAll}, and {@code replaceValues} methods return {@code RandomAccess} lists if the factory
* does. However, the multimap's {@code get} method returns instances of a different class than
* does {@code factory.get()}.
*
* <p>The multimap is serializable if {@code map}, {@code factory}, the lists generated by {@code
* factory}, and the multimap contents are all serializable.
*
* <p>The multimap is not threadsafe when any concurrent operations update the multimap, even if
* {@code map} and the instances generated by {@code factory} are. Concurrent read operations will
* work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link
* #synchronizedListMultimap}.
*
* <p>Call this method only when the simpler methods {@link ArrayListMultimap#create()} and {@link
* LinkedListMultimap#create()} won't suffice.
*
* <p>Note: the multimap assumes complete ownership over of {@code map} and the lists returned by
* {@code factory}. Those objects should not be manually updated, they should be empty when
* provided, and they should not use soft, weak, or phantom references.
*
* @param map place to store the mapping from each key to its corresponding values
* @param factory supplier of new, empty lists that will each hold all values for a given key
* @throws IllegalArgumentException if {@code map} is not empty
*/
public static <K, V> ListMultimap<K, V> newListMultimap(
Map<K, Collection<V>> map, final Supplier<? extends List<V>> factory) {
return new CustomListMultimap<>(map, factory);
}
private static class CustomListMultimap<K, V> extends AbstractListMultimap<K, V> {
transient Supplier<? extends List<V>> factory;
CustomListMultimap(Map<K, Collection<V>> map, Supplier<? extends List<V>> factory) {
super(map);
this.factory = checkNotNull(factory);
}
@Override
Set<K> createKeySet() {
return createMaybeNavigableKeySet();
}
@Override
Map<K, Collection<V>> createAsMap() {
return createMaybeNavigableAsMap();
}
@Override
protected List<V> createCollection() {
return factory.get();
}
@serialData the factory and the backing map
/** @serialData the factory and the backing map */
@GwtIncompatible // java.io.ObjectOutputStream
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeObject(factory);
stream.writeObject(backingMap());
}
@GwtIncompatible // java.io.ObjectInputStream
@SuppressWarnings("unchecked") // reading data stored by writeObject
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
factory = (Supplier<? extends List<V>>) stream.readObject();
Map<K, Collection<V>> map = (Map<K, Collection<V>>) stream.readObject();
setMap(map);
}
@GwtIncompatible // java serialization not supported
private static final long serialVersionUID = 0;
}
Creates a new SetMultimap
that uses the provided map and factory. It can generate a multimap based on arbitrary Map
and Set
classes. The factory
-generated and map
classes determine the multimap iteration order. They also specify the behavior of the equals
, hashCode
, and
toString
methods for the multimap and its returned views. However, the multimap's get
method returns instances of a different class than factory.get()
does.
The multimap is serializable if map
, factory
, the sets generated by
factory
, and the multimap contents are all serializable.
The multimap is not threadsafe when any concurrent operations update the multimap, even if map
and the instances generated by factory
are. Concurrent read operations will work correctly. To allow concurrent update operations, wrap the multimap with a call to synchronizedSetMultimap
.
Call this method only when the simpler methods HashMultimap.create()
, LinkedHashMultimap.create()
, TreeMultimap.create()
, and TreeMultimap.create(Comparator<? super Object>, Comparator<? super Object>)
won't suffice.
Note: the multimap assumes complete ownership over of map
and the sets returned by factory
. Those objects should not be manually updated and they should not use soft, weak, or phantom references.
Params: - map – place to store the mapping from each key to its corresponding values
- factory – supplier of new, empty sets that will each hold all values for a given key
Throws: - IllegalArgumentException – if
map
is not empty
/**
* Creates a new {@code SetMultimap} that uses the provided map and factory. It can generate a
* multimap based on arbitrary {@link Map} and {@link Set} classes.
*
* <p>The {@code factory}-generated and {@code map} classes determine the multimap iteration
* order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code
* toString} methods for the multimap and its returned views. However, the multimap's {@code get}
* method returns instances of a different class than {@code factory.get()} does.
*
* <p>The multimap is serializable if {@code map}, {@code factory}, the sets generated by {@code
* factory}, and the multimap contents are all serializable.
*
* <p>The multimap is not threadsafe when any concurrent operations update the multimap, even if
* {@code map} and the instances generated by {@code factory} are. Concurrent read operations will
* work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link
* #synchronizedSetMultimap}.
*
* <p>Call this method only when the simpler methods {@link HashMultimap#create()}, {@link
* LinkedHashMultimap#create()}, {@link TreeMultimap#create()}, and {@link
* TreeMultimap#create(Comparator, Comparator)} won't suffice.
*
* <p>Note: the multimap assumes complete ownership over of {@code map} and the sets returned by
* {@code factory}. Those objects should not be manually updated and they should not use soft,
* weak, or phantom references.
*
* @param map place to store the mapping from each key to its corresponding values
* @param factory supplier of new, empty sets that will each hold all values for a given key
* @throws IllegalArgumentException if {@code map} is not empty
*/
public static <K, V> SetMultimap<K, V> newSetMultimap(
Map<K, Collection<V>> map, final Supplier<? extends Set<V>> factory) {
return new CustomSetMultimap<>(map, factory);
}
private static class CustomSetMultimap<K, V> extends AbstractSetMultimap<K, V> {
transient Supplier<? extends Set<V>> factory;
CustomSetMultimap(Map<K, Collection<V>> map, Supplier<? extends Set<V>> factory) {
super(map);
this.factory = checkNotNull(factory);
}
@Override
Set<K> createKeySet() {
return createMaybeNavigableKeySet();
}
@Override
Map<K, Collection<V>> createAsMap() {
return createMaybeNavigableAsMap();
}
@Override
protected Set<V> createCollection() {
return factory.get();
}
@Override
<E> Collection<E> unmodifiableCollectionSubclass(Collection<E> collection) {
if (collection instanceof NavigableSet) {
return Sets.unmodifiableNavigableSet((NavigableSet<E>) collection);
} else if (collection instanceof SortedSet) {
return Collections.unmodifiableSortedSet((SortedSet<E>) collection);
} else {
return Collections.unmodifiableSet((Set<E>) collection);
}
}
@Override
Collection<V> wrapCollection(K key, Collection<V> collection) {
if (collection instanceof NavigableSet) {
return new WrappedNavigableSet(key, (NavigableSet<V>) collection, null);
} else if (collection instanceof SortedSet) {
return new WrappedSortedSet(key, (SortedSet<V>) collection, null);
} else {
return new WrappedSet(key, (Set<V>) collection);
}
}
@serialData the factory and the backing map
/** @serialData the factory and the backing map */
@GwtIncompatible // java.io.ObjectOutputStream
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeObject(factory);
stream.writeObject(backingMap());
}
@GwtIncompatible // java.io.ObjectInputStream
@SuppressWarnings("unchecked") // reading data stored by writeObject
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
factory = (Supplier<? extends Set<V>>) stream.readObject();
Map<K, Collection<V>> map = (Map<K, Collection<V>>) stream.readObject();
setMap(map);
}
@GwtIncompatible // not needed in emulated source
private static final long serialVersionUID = 0;
}
Creates a new SortedSetMultimap
that uses the provided map and factory. It can generate a multimap based on arbitrary Map
and SortedSet
classes. The factory
-generated and map
classes determine the multimap iteration order. They also specify the behavior of the equals
, hashCode
, and
toString
methods for the multimap and its returned views. However, the multimap's get
method returns instances of a different class than factory.get()
does.
The multimap is serializable if map
, factory
, the sets generated by
factory
, and the multimap contents are all serializable.
The multimap is not threadsafe when any concurrent operations update the multimap, even if map
and the instances generated by factory
are. Concurrent read operations will work correctly. To allow concurrent update operations, wrap the multimap with a call to synchronizedSortedSetMultimap
.
Call this method only when the simpler methods TreeMultimap.create()
and TreeMultimap.create(Comparator<? super Object>, Comparator<? super Object>)
won't suffice.
Note: the multimap assumes complete ownership over of map
and the sets returned by factory
. Those objects should not be manually updated and they should not use soft, weak, or phantom references.
Params: - map – place to store the mapping from each key to its corresponding values
- factory – supplier of new, empty sorted sets that will each hold all values for a given
key
Throws: - IllegalArgumentException – if
map
is not empty
/**
* Creates a new {@code SortedSetMultimap} that uses the provided map and factory. It can generate
* a multimap based on arbitrary {@link Map} and {@link SortedSet} classes.
*
* <p>The {@code factory}-generated and {@code map} classes determine the multimap iteration
* order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code
* toString} methods for the multimap and its returned views. However, the multimap's {@code get}
* method returns instances of a different class than {@code factory.get()} does.
*
* <p>The multimap is serializable if {@code map}, {@code factory}, the sets generated by {@code
* factory}, and the multimap contents are all serializable.
*
* <p>The multimap is not threadsafe when any concurrent operations update the multimap, even if
* {@code map} and the instances generated by {@code factory} are. Concurrent read operations will
* work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link
* #synchronizedSortedSetMultimap}.
*
* <p>Call this method only when the simpler methods {@link TreeMultimap#create()} and {@link
* TreeMultimap#create(Comparator, Comparator)} won't suffice.
*
* <p>Note: the multimap assumes complete ownership over of {@code map} and the sets returned by
* {@code factory}. Those objects should not be manually updated and they should not use soft,
* weak, or phantom references.
*
* @param map place to store the mapping from each key to its corresponding values
* @param factory supplier of new, empty sorted sets that will each hold all values for a given
* key
* @throws IllegalArgumentException if {@code map} is not empty
*/
public static <K, V> SortedSetMultimap<K, V> newSortedSetMultimap(
Map<K, Collection<V>> map, final Supplier<? extends SortedSet<V>> factory) {
return new CustomSortedSetMultimap<>(map, factory);
}
private static class CustomSortedSetMultimap<K, V> extends AbstractSortedSetMultimap<K, V> {
transient Supplier<? extends SortedSet<V>> factory;
transient Comparator<? super V> valueComparator;
CustomSortedSetMultimap(Map<K, Collection<V>> map, Supplier<? extends SortedSet<V>> factory) {
super(map);
this.factory = checkNotNull(factory);
valueComparator = factory.get().comparator();
}
@Override
Set<K> createKeySet() {
return createMaybeNavigableKeySet();
}
@Override
Map<K, Collection<V>> createAsMap() {
return createMaybeNavigableAsMap();
}
@Override
protected SortedSet<V> createCollection() {
return factory.get();
}
@Override
public Comparator<? super V> valueComparator() {
return valueComparator;
}
@serialData the factory and the backing map
/** @serialData the factory and the backing map */
@GwtIncompatible // java.io.ObjectOutputStream
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeObject(factory);
stream.writeObject(backingMap());
}
@GwtIncompatible // java.io.ObjectInputStream
@SuppressWarnings("unchecked") // reading data stored by writeObject
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
factory = (Supplier<? extends SortedSet<V>>) stream.readObject();
valueComparator = factory.get().comparator();
Map<K, Collection<V>> map = (Map<K, Collection<V>>) stream.readObject();
setMap(map);
}
@GwtIncompatible // not needed in emulated source
private static final long serialVersionUID = 0;
}
Copies each key-value mapping in source
into dest
, with its key and value reversed. If source
is an ImmutableMultimap
, consider using ImmutableMultimap.inverse
instead.
Params: - source – any multimap
- dest – the multimap to copy into; usually empty
Returns: dest
/**
* Copies each key-value mapping in {@code source} into {@code dest}, with its key and value
* reversed.
*
* <p>If {@code source} is an {@link ImmutableMultimap}, consider using {@link
* ImmutableMultimap#inverse} instead.
*
* @param source any multimap
* @param dest the multimap to copy into; usually empty
* @return {@code dest}
*/
@CanIgnoreReturnValue
public static <K, V, M extends Multimap<K, V>> M invertFrom(
Multimap<? extends V, ? extends K> source, M dest) {
checkNotNull(dest);
for (Map.Entry<? extends V, ? extends K> entry : source.entries()) {
dest.put(entry.getValue(), entry.getKey());
}
return dest;
}
Returns a synchronized (thread-safe) multimap backed by the specified multimap. In order to
guarantee serial access, it is critical that all access to the backing multimap is
accomplished through the returned multimap.
It is imperative that the user manually synchronize on the returned multimap when accessing
any of its collection views:
Multimap<K, V> multimap = Multimaps.synchronizedMultimap(
HashMultimap.<K, V>create());
...
Collection<V> values = multimap.get(key); // Needn't be in synchronized block
...
synchronized (multimap) { // Synchronizing on multimap, not values!
Iterator<V> i = values.iterator(); // Must be in synchronized block
while (i.hasNext()) {
foo(i.next());
}
}
Failure to follow this advice may result in non-deterministic behavior.
Note that the generated multimap's Multimap.removeAll
and Multimap.replaceValues
methods return collections that aren't synchronized.
The returned multimap will be serializable if the specified multimap is serializable.
Params: - multimap – the multimap to be wrapped in a synchronized view
Returns: a synchronized view of the specified multimap
/**
* Returns a synchronized (thread-safe) multimap backed by the specified multimap. In order to
* guarantee serial access, it is critical that <b>all</b> access to the backing multimap is
* accomplished through the returned multimap.
*
* <p>It is imperative that the user manually synchronize on the returned multimap when accessing
* any of its collection views:
*
* <pre>{@code
* Multimap<K, V> multimap = Multimaps.synchronizedMultimap(
* HashMultimap.<K, V>create());
* ...
* Collection<V> values = multimap.get(key); // Needn't be in synchronized block
* ...
* synchronized (multimap) { // Synchronizing on multimap, not values!
* Iterator<V> i = values.iterator(); // Must be in synchronized block
* while (i.hasNext()) {
* foo(i.next());
* }
* }
* }</pre>
*
* <p>Failure to follow this advice may result in non-deterministic behavior.
*
* <p>Note that the generated multimap's {@link Multimap#removeAll} and {@link
* Multimap#replaceValues} methods return collections that aren't synchronized.
*
* <p>The returned multimap will be serializable if the specified multimap is serializable.
*
* @param multimap the multimap to be wrapped in a synchronized view
* @return a synchronized view of the specified multimap
*/
public static <K, V> Multimap<K, V> synchronizedMultimap(Multimap<K, V> multimap) {
return Synchronized.multimap(multimap, null);
}
Returns an unmodifiable view of the specified multimap. Query operations on the returned multimap "read through" to the specified multimap, and attempts to modify the returned multimap, either directly or through the multimap's views, result in an
UnsupportedOperationException
. Note that the generated multimap's Multimap.removeAll
and Multimap.replaceValues
methods return collections that are modifiable.
The returned multimap will be serializable if the specified multimap is serializable.
Params: - delegate – the multimap for which an unmodifiable view is to be returned
Returns: an unmodifiable view of the specified multimap
/**
* Returns an unmodifiable view of the specified multimap. Query operations on the returned
* multimap "read through" to the specified multimap, and attempts to modify the returned
* multimap, either directly or through the multimap's views, result in an {@code
* UnsupportedOperationException}.
*
* <p>Note that the generated multimap's {@link Multimap#removeAll} and {@link
* Multimap#replaceValues} methods return collections that are modifiable.
*
* <p>The returned multimap will be serializable if the specified multimap is serializable.
*
* @param delegate the multimap for which an unmodifiable view is to be returned
* @return an unmodifiable view of the specified multimap
*/
public static <K, V> Multimap<K, V> unmodifiableMultimap(Multimap<K, V> delegate) {
if (delegate instanceof UnmodifiableMultimap || delegate instanceof ImmutableMultimap) {
return delegate;
}
return new UnmodifiableMultimap<>(delegate);
}
Simply returns its argument.
Deprecated: no need to use this Since: 10.0
/**
* Simply returns its argument.
*
* @deprecated no need to use this
* @since 10.0
*/
@Deprecated
public static <K, V> Multimap<K, V> unmodifiableMultimap(ImmutableMultimap<K, V> delegate) {
return checkNotNull(delegate);
}
private static class UnmodifiableMultimap<K, V> extends ForwardingMultimap<K, V>
implements Serializable {
final Multimap<K, V> delegate;
@MonotonicNonNull transient Collection<Entry<K, V>> entries;
@MonotonicNonNull transient Multiset<K> keys;
@MonotonicNonNull transient Set<K> keySet;
@MonotonicNonNull transient Collection<V> values;
@MonotonicNonNull transient Map<K, Collection<V>> map;
UnmodifiableMultimap(final Multimap<K, V> delegate) {
this.delegate = checkNotNull(delegate);
}
@Override
protected Multimap<K, V> delegate() {
return delegate;
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Map<K, Collection<V>> asMap() {
Map<K, Collection<V>> result = map;
if (result == null) {
result =
map =
Collections.unmodifiableMap(
Maps.transformValues(
delegate.asMap(),
new Function<Collection<V>, Collection<V>>() {
@Override
public Collection<V> apply(Collection<V> collection) {
return unmodifiableValueCollection(collection);
}
}));
}
return result;
}
@Override
public Collection<Entry<K, V>> entries() {
Collection<Entry<K, V>> result = entries;
if (result == null) {
entries = result = unmodifiableEntries(delegate.entries());
}
return result;
}
@Override
public Collection<V> get(K key) {
return unmodifiableValueCollection(delegate.get(key));
}
@Override
public Multiset<K> keys() {
Multiset<K> result = keys;
if (result == null) {
keys = result = Multisets.unmodifiableMultiset(delegate.keys());
}
return result;
}
@Override
public Set<K> keySet() {
Set<K> result = keySet;
if (result == null) {
keySet = result = Collections.unmodifiableSet(delegate.keySet());
}
return result;
}
@Override
public boolean put(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public boolean putAll(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
@Override
public boolean putAll(Multimap<? extends K, ? extends V> multimap) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
@Override
public Collection<V> removeAll(Object key) {
throw new UnsupportedOperationException();
}
@Override
public Collection<V> replaceValues(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
@Override
public Collection<V> values() {
Collection<V> result = values;
if (result == null) {
values = result = Collections.unmodifiableCollection(delegate.values());
}
return result;
}
private static final long serialVersionUID = 0;
}
private static class UnmodifiableListMultimap<K, V> extends UnmodifiableMultimap<K, V>
implements ListMultimap<K, V> {
UnmodifiableListMultimap(ListMultimap<K, V> delegate) {
super(delegate);
}
@Override
public ListMultimap<K, V> delegate() {
return (ListMultimap<K, V>) super.delegate();
}
@Override
public List<V> get(K key) {
return Collections.unmodifiableList(delegate().get(key));
}
@Override
public List<V> removeAll(Object key) {
throw new UnsupportedOperationException();
}
@Override
public List<V> replaceValues(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
private static final long serialVersionUID = 0;
}
private static class UnmodifiableSetMultimap<K, V> extends UnmodifiableMultimap<K, V>
implements SetMultimap<K, V> {
UnmodifiableSetMultimap(SetMultimap<K, V> delegate) {
super(delegate);
}
@Override
public SetMultimap<K, V> delegate() {
return (SetMultimap<K, V>) super.delegate();
}
@Override
public Set<V> get(K key) {
/*
* Note that this doesn't return a SortedSet when delegate is a
* SortedSetMultiset, unlike (SortedSet<V>) super.get().
*/
return Collections.unmodifiableSet(delegate().get(key));
}
@Override
public Set<Map.Entry<K, V>> entries() {
return Maps.unmodifiableEntrySet(delegate().entries());
}
@Override
public Set<V> removeAll(Object key) {
throw new UnsupportedOperationException();
}
@Override
public Set<V> replaceValues(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
private static final long serialVersionUID = 0;
}
private static class UnmodifiableSortedSetMultimap<K, V> extends UnmodifiableSetMultimap<K, V>
implements SortedSetMultimap<K, V> {
UnmodifiableSortedSetMultimap(SortedSetMultimap<K, V> delegate) {
super(delegate);
}
@Override
public SortedSetMultimap<K, V> delegate() {
return (SortedSetMultimap<K, V>) super.delegate();
}
@Override
public SortedSet<V> get(K key) {
return Collections.unmodifiableSortedSet(delegate().get(key));
}
@Override
public SortedSet<V> removeAll(Object key) {
throw new UnsupportedOperationException();
}
@Override
public SortedSet<V> replaceValues(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
@Override
public Comparator<? super V> valueComparator() {
return delegate().valueComparator();
}
private static final long serialVersionUID = 0;
}
Returns a synchronized (thread-safe) SetMultimap
backed by the specified multimap. You must follow the warnings described in synchronizedMultimap
.
The returned multimap will be serializable if the specified multimap is serializable.
Params: - multimap – the multimap to be wrapped
Returns: a synchronized view of the specified multimap
/**
* Returns a synchronized (thread-safe) {@code SetMultimap} backed by the specified multimap.
*
* <p>You must follow the warnings described in {@link #synchronizedMultimap}.
*
* <p>The returned multimap will be serializable if the specified multimap is serializable.
*
* @param multimap the multimap to be wrapped
* @return a synchronized view of the specified multimap
*/
public static <K, V> SetMultimap<K, V> synchronizedSetMultimap(SetMultimap<K, V> multimap) {
return Synchronized.setMultimap(multimap, null);
}
Returns an unmodifiable view of the specified SetMultimap
. Query operations on the returned multimap "read through" to the specified multimap, and attempts to modify the returned multimap, either directly or through the multimap's views, result in an
UnsupportedOperationException
. Note that the generated multimap's Multimap.removeAll
and Multimap.replaceValues
methods return collections that are modifiable.
The returned multimap will be serializable if the specified multimap is serializable.
Params: - delegate – the multimap for which an unmodifiable view is to be returned
Returns: an unmodifiable view of the specified multimap
/**
* Returns an unmodifiable view of the specified {@code SetMultimap}. Query operations on the
* returned multimap "read through" to the specified multimap, and attempts to modify the returned
* multimap, either directly or through the multimap's views, result in an {@code
* UnsupportedOperationException}.
*
* <p>Note that the generated multimap's {@link Multimap#removeAll} and {@link
* Multimap#replaceValues} methods return collections that are modifiable.
*
* <p>The returned multimap will be serializable if the specified multimap is serializable.
*
* @param delegate the multimap for which an unmodifiable view is to be returned
* @return an unmodifiable view of the specified multimap
*/
public static <K, V> SetMultimap<K, V> unmodifiableSetMultimap(SetMultimap<K, V> delegate) {
if (delegate instanceof UnmodifiableSetMultimap || delegate instanceof ImmutableSetMultimap) {
return delegate;
}
return new UnmodifiableSetMultimap<>(delegate);
}
Simply returns its argument.
Deprecated: no need to use this Since: 10.0
/**
* Simply returns its argument.
*
* @deprecated no need to use this
* @since 10.0
*/
@Deprecated
public static <K, V> SetMultimap<K, V> unmodifiableSetMultimap(
ImmutableSetMultimap<K, V> delegate) {
return checkNotNull(delegate);
}
Returns a synchronized (thread-safe) SortedSetMultimap
backed by the specified multimap. You must follow the warnings described in synchronizedMultimap
.
The returned multimap will be serializable if the specified multimap is serializable.
Params: - multimap – the multimap to be wrapped
Returns: a synchronized view of the specified multimap
/**
* Returns a synchronized (thread-safe) {@code SortedSetMultimap} backed by the specified
* multimap.
*
* <p>You must follow the warnings described in {@link #synchronizedMultimap}.
*
* <p>The returned multimap will be serializable if the specified multimap is serializable.
*
* @param multimap the multimap to be wrapped
* @return a synchronized view of the specified multimap
*/
public static <K, V> SortedSetMultimap<K, V> synchronizedSortedSetMultimap(
SortedSetMultimap<K, V> multimap) {
return Synchronized.sortedSetMultimap(multimap, null);
}
Returns an unmodifiable view of the specified SortedSetMultimap
. Query operations on the returned multimap "read through" to the specified multimap, and attempts to modify the returned multimap, either directly or through the multimap's views, result in an
UnsupportedOperationException
. Note that the generated multimap's Multimap.removeAll
and Multimap.replaceValues
methods return collections that are modifiable.
The returned multimap will be serializable if the specified multimap is serializable.
Params: - delegate – the multimap for which an unmodifiable view is to be returned
Returns: an unmodifiable view of the specified multimap
/**
* Returns an unmodifiable view of the specified {@code SortedSetMultimap}. Query operations on
* the returned multimap "read through" to the specified multimap, and attempts to modify the
* returned multimap, either directly or through the multimap's views, result in an {@code
* UnsupportedOperationException}.
*
* <p>Note that the generated multimap's {@link Multimap#removeAll} and {@link
* Multimap#replaceValues} methods return collections that are modifiable.
*
* <p>The returned multimap will be serializable if the specified multimap is serializable.
*
* @param delegate the multimap for which an unmodifiable view is to be returned
* @return an unmodifiable view of the specified multimap
*/
public static <K, V> SortedSetMultimap<K, V> unmodifiableSortedSetMultimap(
SortedSetMultimap<K, V> delegate) {
if (delegate instanceof UnmodifiableSortedSetMultimap) {
return delegate;
}
return new UnmodifiableSortedSetMultimap<>(delegate);
}
Returns a synchronized (thread-safe) ListMultimap
backed by the specified multimap. You must follow the warnings described in synchronizedMultimap
.
Params: - multimap – the multimap to be wrapped
Returns: a synchronized view of the specified multimap
/**
* Returns a synchronized (thread-safe) {@code ListMultimap} backed by the specified multimap.
*
* <p>You must follow the warnings described in {@link #synchronizedMultimap}.
*
* @param multimap the multimap to be wrapped
* @return a synchronized view of the specified multimap
*/
public static <K, V> ListMultimap<K, V> synchronizedListMultimap(ListMultimap<K, V> multimap) {
return Synchronized.listMultimap(multimap, null);
}
Returns an unmodifiable view of the specified ListMultimap
. Query operations on the returned multimap "read through" to the specified multimap, and attempts to modify the returned multimap, either directly or through the multimap's views, result in an
UnsupportedOperationException
. Note that the generated multimap's Multimap.removeAll
and Multimap.replaceValues
methods return collections that are modifiable.
The returned multimap will be serializable if the specified multimap is serializable.
Params: - delegate – the multimap for which an unmodifiable view is to be returned
Returns: an unmodifiable view of the specified multimap
/**
* Returns an unmodifiable view of the specified {@code ListMultimap}. Query operations on the
* returned multimap "read through" to the specified multimap, and attempts to modify the returned
* multimap, either directly or through the multimap's views, result in an {@code
* UnsupportedOperationException}.
*
* <p>Note that the generated multimap's {@link Multimap#removeAll} and {@link
* Multimap#replaceValues} methods return collections that are modifiable.
*
* <p>The returned multimap will be serializable if the specified multimap is serializable.
*
* @param delegate the multimap for which an unmodifiable view is to be returned
* @return an unmodifiable view of the specified multimap
*/
public static <K, V> ListMultimap<K, V> unmodifiableListMultimap(ListMultimap<K, V> delegate) {
if (delegate instanceof UnmodifiableListMultimap || delegate instanceof ImmutableListMultimap) {
return delegate;
}
return new UnmodifiableListMultimap<>(delegate);
}
Simply returns its argument.
Deprecated: no need to use this Since: 10.0
/**
* Simply returns its argument.
*
* @deprecated no need to use this
* @since 10.0
*/
@Deprecated
public static <K, V> ListMultimap<K, V> unmodifiableListMultimap(
ImmutableListMultimap<K, V> delegate) {
return checkNotNull(delegate);
}
Returns an unmodifiable view of the specified collection, preserving the interface for instances of SortedSet
, Set
, List
and Collection
, in that order of preference. Params: - collection – the collection for which to return an unmodifiable view
Returns: an unmodifiable view of the collection
/**
* Returns an unmodifiable view of the specified collection, preserving the interface for
* instances of {@code SortedSet}, {@code Set}, {@code List} and {@code Collection}, in that order
* of preference.
*
* @param collection the collection for which to return an unmodifiable view
* @return an unmodifiable view of the collection
*/
private static <V> Collection<V> unmodifiableValueCollection(Collection<V> collection) {
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);
}
return Collections.unmodifiableCollection(collection);
}
Returns an unmodifiable view of the specified collection of entries. The Entry.setValue
operation throws an UnsupportedOperationException
. If the specified collection is a Set
, the returned collection is also a Set
. Params: - entries – the entries for which to return an unmodifiable view
Returns: an unmodifiable view of the entries
/**
* Returns an unmodifiable view of the specified collection of entries. The {@link Entry#setValue}
* operation throws an {@link UnsupportedOperationException}. If the specified collection is a
* {@code Set}, the returned collection is also a {@code Set}.
*
* @param entries the entries for which to return an unmodifiable view
* @return an unmodifiable view of the entries
*/
private static <K, V> Collection<Entry<K, V>> unmodifiableEntries(
Collection<Entry<K, V>> entries) {
if (entries instanceof Set) {
return Maps.unmodifiableEntrySet((Set<Entry<K, V>>) entries);
}
return new Maps.UnmodifiableEntries<>(Collections.unmodifiableCollection(entries));
}
Since: 15.0
/**
* Returns {@link ListMultimap#asMap multimap.asMap()}, with its type corrected from {@code Map<K,
* Collection<V>>} to {@code Map<K, List<V>>}.
*
* @since 15.0
*/
@Beta
@SuppressWarnings("unchecked")
// safe by specification of ListMultimap.asMap()
public static <K, V> Map<K, List<V>> asMap(ListMultimap<K, V> multimap) {
return (Map<K, List<V>>) (Map<K, ?>) multimap.asMap();
}
Since: 15.0
/**
* Returns {@link SetMultimap#asMap multimap.asMap()}, with its type corrected from {@code Map<K,
* Collection<V>>} to {@code Map<K, Set<V>>}.
*
* @since 15.0
*/
@Beta
@SuppressWarnings("unchecked")
// safe by specification of SetMultimap.asMap()
public static <K, V> Map<K, Set<V>> asMap(SetMultimap<K, V> multimap) {
return (Map<K, Set<V>>) (Map<K, ?>) multimap.asMap();
}
Returns multimap.asMap()
, with its type corrected from
Map<K, Collection<V>>
to Map<K, SortedSet<V>>
. Since: 15.0
/**
* Returns {@link SortedSetMultimap#asMap multimap.asMap()}, with its type corrected from {@code
* Map<K, Collection<V>>} to {@code Map<K, SortedSet<V>>}.
*
* @since 15.0
*/
@Beta
@SuppressWarnings("unchecked")
// safe by specification of SortedSetMultimap.asMap()
public static <K, V> Map<K, SortedSet<V>> asMap(SortedSetMultimap<K, V> multimap) {
return (Map<K, SortedSet<V>>) (Map<K, ?>) multimap.asMap();
}
Returns multimap.asMap()
. This is provided for parity with the other more strongly-typed asMap()
implementations. Since: 15.0
/**
* Returns {@link Multimap#asMap multimap.asMap()}. This is provided for parity with the other
* more strongly-typed {@code asMap()} implementations.
*
* @since 15.0
*/
@Beta
public static <K, V> Map<K, Collection<V>> asMap(Multimap<K, V> multimap) {
return multimap.asMap();
}
Returns a multimap view of the specified map. The multimap is backed by the map, so changes to the map are reflected in the multimap, and vice versa. If the map is modified while an iteration over one of the multimap's collection views is in progress (except through the iterator's own remove
operation, or through the setValue
operation on a map entry returned by the iterator), the results of the iteration are undefined. The multimap supports mapping removal, which removes the corresponding mapping from the map. It does not support any operations which might add mappings, such as put
,
putAll
or replaceValues
.
The returned multimap will be serializable if the specified map is serializable.
Params: - map – the backing map for the returned multimap view
/**
* Returns a multimap view of the specified map. The multimap is backed by the map, so changes to
* the map are reflected in the multimap, and vice versa. If the map is modified while an
* iteration over one of the multimap's collection views is in progress (except through the
* iterator's own {@code remove} operation, or through the {@code setValue} operation on a map
* entry returned by the iterator), the results of the iteration are undefined.
*
* <p>The multimap supports mapping removal, which removes the corresponding mapping from the map.
* It does not support any operations which might add mappings, such as {@code put}, {@code
* putAll} or {@code replaceValues}.
*
* <p>The returned multimap will be serializable if the specified map is serializable.
*
* @param map the backing map for the returned multimap view
*/
public static <K, V> SetMultimap<K, V> forMap(Map<K, V> map) {
return new MapMultimap<>(map);
}
See Also:
/** @see Multimaps#forMap */
private static class MapMultimap<K, V> extends AbstractMultimap<K, V>
implements SetMultimap<K, V>, Serializable {
final Map<K, V> map;
MapMultimap(Map<K, V> map) {
this.map = checkNotNull(map);
}
@Override
public int size() {
return map.size();
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public boolean containsEntry(Object key, Object value) {
return map.entrySet().contains(Maps.immutableEntry(key, value));
}
@Override
public Set<V> get(final K key) {
return new Sets.ImprovedAbstractSet<V>() {
@Override
public Iterator<V> iterator() {
return new Iterator<V>() {
int i;
@Override
public boolean hasNext() {
return (i == 0) && map.containsKey(key);
}
@Override
public V next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
i++;
return map.get(key);
}
@Override
public void remove() {
checkRemove(i == 1);
i = -1;
map.remove(key);
}
};
}
@Override
public int size() {
return map.containsKey(key) ? 1 : 0;
}
};
}
@Override
public boolean put(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public boolean putAll(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
@Override
public boolean putAll(Multimap<? extends K, ? extends V> multimap) {
throw new UnsupportedOperationException();
}
@Override
public Set<V> replaceValues(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object key, Object value) {
return map.entrySet().remove(Maps.immutableEntry(key, value));
}
@Override
public Set<V> removeAll(Object key) {
Set<V> values = new HashSet<V>(2);
if (!map.containsKey(key)) {
return values;
}
values.add(map.remove(key));
return values;
}
@Override
public void clear() {
map.clear();
}
@Override
Set<K> createKeySet() {
return map.keySet();
}
@Override
Collection<V> createValues() {
return map.values();
}
@Override
public Set<Entry<K, V>> entries() {
return map.entrySet();
}
@Override
Collection<Entry<K, V>> createEntries() {
throw new AssertionError("unreachable");
}
@Override
Multiset<K> createKeys() {
return new Multimaps.Keys<K, V>(this);
}
@Override
Iterator<Entry<K, V>> entryIterator() {
return map.entrySet().iterator();
}
@Override
Map<K, Collection<V>> createAsMap() {
return new AsMap<>(this);
}
@Override
public int hashCode() {
return map.hashCode();
}
private static final long serialVersionUID = 7845222491160860175L;
}
Returns a view of a multimap where each value is transformed by a function. All other
properties of the multimap, such as iteration order, are left intact. For example, the code:
Multimap<String, Integer> multimap =
ImmutableSetMultimap.of("a", 2, "b", -3, "b", -3, "a", 4, "c", 6);
Function<Integer, String> square = new Function<Integer, String>() {
public String apply(Integer in) {
return Integer.toString(in * in);
}
};
Multimap<String, String> transformed =
Multimaps.transformValues(multimap, square);
System.out.println(transformed);
... prints {a=[4, 16], b=[9, 9], c=[36]}
. Changes in the underlying multimap are reflected in this view. Conversely, this view
supports removal operations, and these are reflected in the underlying multimap.
It's acceptable for the underlying multimap to contain null keys, and even null values
provided that the function is capable of accepting null input. The transformed multimap might
contain null values, if the function sometimes gives a null result.
The returned multimap is not thread-safe or serializable, even if the underlying multimap is. The equals
and hashCode
methods of the returned multimap are meaningless, since there is not a definition of equals
or hashCode
for general collections, and get()
will return a general Collection
as opposed to a List
or a Set
.
The function is applied lazily, invoked when needed. This is necessary for the returned multimap to be a view, but it means that the function will be applied many times for bulk operations like Multimap.containsValue
and Multimap.toString()
. For this to perform well, function
should be fast. To avoid lazy evaluation when the returned multimap doesn't need to be a view, copy the returned multimap into a new multimap of your choosing.
Since: 7.0
/**
* Returns a view of a multimap where each value is transformed by a function. All other
* properties of the multimap, such as iteration order, are left intact. For example, the code:
*
* <pre>{@code
* Multimap<String, Integer> multimap =
* ImmutableSetMultimap.of("a", 2, "b", -3, "b", -3, "a", 4, "c", 6);
* Function<Integer, String> square = new Function<Integer, String>() {
* public String apply(Integer in) {
* return Integer.toString(in * in);
* }
* };
* Multimap<String, String> transformed =
* Multimaps.transformValues(multimap, square);
* System.out.println(transformed);
* }</pre>
*
* ... prints {@code {a=[4, 16], b=[9, 9], c=[36]}}.
*
* <p>Changes in the underlying multimap are reflected in this view. Conversely, this view
* supports removal operations, and these are reflected in the underlying multimap.
*
* <p>It's acceptable for the underlying multimap to contain null keys, and even null values
* provided that the function is capable of accepting null input. The transformed multimap might
* contain null values, if the function sometimes gives a null result.
*
* <p>The returned multimap is not thread-safe or serializable, even if the underlying multimap
* is. The {@code equals} and {@code hashCode} methods of the returned multimap are meaningless,
* since there is not a definition of {@code equals} or {@code hashCode} for general collections,
* and {@code get()} will return a general {@code Collection} as opposed to a {@code List} or a
* {@code Set}.
*
* <p>The function is applied lazily, invoked when needed. This is necessary for the returned
* multimap to be a view, but it means that the function will be applied many times for bulk
* operations like {@link Multimap#containsValue} and {@code Multimap.toString()}. For this to
* perform well, {@code function} should be fast. To avoid lazy evaluation when the returned
* multimap doesn't need to be a view, copy the returned multimap into a new multimap of your
* choosing.
*
* @since 7.0
*/
public static <K, V1, V2> Multimap<K, V2> transformValues(
Multimap<K, V1> fromMultimap, final Function<? super V1, V2> function) {
checkNotNull(function);
EntryTransformer<K, V1, V2> transformer = Maps.asEntryTransformer(function);
return transformEntries(fromMultimap, transformer);
}
Returns a view of a ListMultimap
where each value is transformed by a function. All other properties of the multimap, such as iteration order, are left intact. For example, the code:
ListMultimap<String, Integer> multimap
= ImmutableListMultimap.of("a", 4, "a", 16, "b", 9);
Function<Integer, Double> sqrt =
new Function<Integer, Double>() {
public Double apply(Integer in) {
return Math.sqrt((int) in);
}
};
ListMultimap<String, Double> transformed = Multimaps.transformValues(map,
sqrt);
System.out.println(transformed);
... prints {a=[2.0, 4.0], b=[3.0]}
. Changes in the underlying multimap are reflected in this view. Conversely, this view
supports removal operations, and these are reflected in the underlying multimap.
It's acceptable for the underlying multimap to contain null keys, and even null values
provided that the function is capable of accepting null input. The transformed multimap might
contain null values, if the function sometimes gives a null result.
The returned multimap is not thread-safe or serializable, even if the underlying multimap
is.
The function is applied lazily, invoked when needed. This is necessary for the returned multimap to be a view, but it means that the function will be applied many times for bulk operations like Multimap.containsValue
and Multimap.toString()
. For this to perform well, function
should be fast. To avoid lazy evaluation when the returned multimap doesn't need to be a view, copy the returned multimap into a new multimap of your choosing.
Since: 7.0
/**
* Returns a view of a {@code ListMultimap} where each value is transformed by a function. All
* other properties of the multimap, such as iteration order, are left intact. For example, the
* code:
*
* <pre>{@code
* ListMultimap<String, Integer> multimap
* = ImmutableListMultimap.of("a", 4, "a", 16, "b", 9);
* Function<Integer, Double> sqrt =
* new Function<Integer, Double>() {
* public Double apply(Integer in) {
* return Math.sqrt((int) in);
* }
* };
* ListMultimap<String, Double> transformed = Multimaps.transformValues(map,
* sqrt);
* System.out.println(transformed);
* }</pre>
*
* ... prints {@code {a=[2.0, 4.0], b=[3.0]}}.
*
* <p>Changes in the underlying multimap are reflected in this view. Conversely, this view
* supports removal operations, and these are reflected in the underlying multimap.
*
* <p>It's acceptable for the underlying multimap to contain null keys, and even null values
* provided that the function is capable of accepting null input. The transformed multimap might
* contain null values, if the function sometimes gives a null result.
*
* <p>The returned multimap is not thread-safe or serializable, even if the underlying multimap
* is.
*
* <p>The function is applied lazily, invoked when needed. This is necessary for the returned
* multimap to be a view, but it means that the function will be applied many times for bulk
* operations like {@link Multimap#containsValue} and {@code Multimap.toString()}. For this to
* perform well, {@code function} should be fast. To avoid lazy evaluation when the returned
* multimap doesn't need to be a view, copy the returned multimap into a new multimap of your
* choosing.
*
* @since 7.0
*/
public static <K, V1, V2> ListMultimap<K, V2> transformValues(
ListMultimap<K, V1> fromMultimap, final Function<? super V1, V2> function) {
checkNotNull(function);
EntryTransformer<K, V1, V2> transformer = Maps.asEntryTransformer(function);
return transformEntries(fromMultimap, transformer);
}
Returns a view of a multimap whose values are derived from the original multimap's entries. In contrast to transformValues
, this method's entry-transformation logic may depend on the key as well as the value. All other properties of the transformed multimap, such as iteration order, are left intact.
For example, the code:
SetMultimap<String, Integer> multimap =
ImmutableSetMultimap.of("a", 1, "a", 4, "b", -6);
EntryTransformer<String, Integer, String> transformer =
new EntryTransformer<String, Integer, String>() {
public String transformEntry(String key, Integer value) {
return (value >= 0) ? key : "no" + key;
}
};
Multimap<String, String> transformed =
Multimaps.transformEntries(multimap, transformer);
System.out.println(transformed);
... prints {a=[a, a], b=[nob]}
. Changes in the underlying multimap are reflected in this view. Conversely, this view
supports removal operations, and these are reflected in the underlying multimap.
It's acceptable for the underlying multimap to contain null keys and null values provided
that the transformer is capable of accepting null inputs. The transformed multimap might
contain null values if the transformer sometimes gives a null result.
The returned multimap is not thread-safe or serializable, even if the underlying multimap is. The equals
and hashCode
methods of the returned multimap are meaningless, since there is not a definition of equals
or hashCode
for general collections, and get()
will return a general Collection
as opposed to a List
or a Set
.
The transformer is applied lazily, invoked when needed. This is necessary for the returned multimap to be a view, but it means that the transformer will be applied many times for bulk operations like Multimap.containsValue
and Object.toString
. For this to perform well, transformer
should be fast. To avoid lazy evaluation when the returned multimap doesn't need to be a view, copy the returned multimap into a new multimap of your choosing.
Warning: This method assumes that for any instance k
of
EntryTransformer
key type K
, k.equals(k2)
implies that k2
is also of type K
. Using an EntryTransformer
key type for which this may not hold, such as ArrayList
, may risk a ClassCastException
when calling methods on the transformed multimap.
Since: 7.0
/**
* Returns a view of a multimap whose values are derived from the original multimap's entries. In
* contrast to {@link #transformValues}, this method's entry-transformation logic may depend on
* the key as well as the value.
*
* <p>All other properties of the transformed multimap, such as iteration order, are left intact.
* For example, the code:
*
* <pre>{@code
* SetMultimap<String, Integer> multimap =
* ImmutableSetMultimap.of("a", 1, "a", 4, "b", -6);
* EntryTransformer<String, Integer, String> transformer =
* new EntryTransformer<String, Integer, String>() {
* public String transformEntry(String key, Integer value) {
* return (value >= 0) ? key : "no" + key;
* }
* };
* Multimap<String, String> transformed =
* Multimaps.transformEntries(multimap, transformer);
* System.out.println(transformed);
* }</pre>
*
* ... prints {@code {a=[a, a], b=[nob]}}.
*
* <p>Changes in the underlying multimap are reflected in this view. Conversely, this view
* supports removal operations, and these are reflected in the underlying multimap.
*
* <p>It's acceptable for the underlying multimap to contain null keys and null values provided
* that the transformer is capable of accepting null inputs. The transformed multimap might
* contain null values if the transformer sometimes gives a null result.
*
* <p>The returned multimap is not thread-safe or serializable, even if the underlying multimap
* is. The {@code equals} and {@code hashCode} methods of the returned multimap are meaningless,
* since there is not a definition of {@code equals} or {@code hashCode} for general collections,
* and {@code get()} will return a general {@code Collection} as opposed to a {@code List} or a
* {@code Set}.
*
* <p>The transformer is applied lazily, invoked when needed. This is necessary for the returned
* multimap to be a view, but it means that the transformer will be applied many times for bulk
* operations like {@link Multimap#containsValue} and {@link Object#toString}. For this to perform
* well, {@code transformer} should be fast. To avoid lazy evaluation when the returned multimap
* doesn't need to be a view, copy the returned multimap into a new multimap of your choosing.
*
* <p><b>Warning:</b> This method assumes that for any instance {@code k} of {@code
* EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of
* type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as
* {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the
* transformed multimap.
*
* @since 7.0
*/
public static <K, V1, V2> Multimap<K, V2> transformEntries(
Multimap<K, V1> fromMap, EntryTransformer<? super K, ? super V1, V2> transformer) {
return new TransformedEntriesMultimap<>(fromMap, transformer);
}
Returns a view of a ListMultimap
whose values are derived from the original multimap's entries. In contrast to transformValues(ListMultimap<Object,Object>, Function<? super Object,Object>)
, this method's entry-transformation logic may depend on the key as well as the value. All other properties of the transformed multimap, such as iteration order, are left intact.
For example, the code:
Multimap<String, Integer> multimap =
ImmutableMultimap.of("a", 1, "a", 4, "b", 6);
EntryTransformer<String, Integer, String> transformer =
new EntryTransformer<String, Integer, String>() {
public String transformEntry(String key, Integer value) {
return key + value;
}
};
Multimap<String, String> transformed =
Multimaps.transformEntries(multimap, transformer);
System.out.println(transformed);
... prints {"a"=["a1", "a4"], "b"=["b6"]}
. Changes in the underlying multimap are reflected in this view. Conversely, this view
supports removal operations, and these are reflected in the underlying multimap.
It's acceptable for the underlying multimap to contain null keys and null values provided
that the transformer is capable of accepting null inputs. The transformed multimap might
contain null values if the transformer sometimes gives a null result.
The returned multimap is not thread-safe or serializable, even if the underlying multimap
is.
The transformer is applied lazily, invoked when needed. This is necessary for the returned multimap to be a view, but it means that the transformer will be applied many times for bulk operations like Multimap.containsValue
and Object.toString
. For this to perform well, transformer
should be fast. To avoid lazy evaluation when the returned multimap doesn't need to be a view, copy the returned multimap into a new multimap of your choosing.
Warning: This method assumes that for any instance k
of
EntryTransformer
key type K
, k.equals(k2)
implies that k2
is also of type K
. Using an EntryTransformer
key type for which this may not hold, such as ArrayList
, may risk a ClassCastException
when calling methods on the transformed multimap.
Since: 7.0
/**
* Returns a view of a {@code ListMultimap} whose values are derived from the original multimap's
* entries. In contrast to {@link #transformValues(ListMultimap, Function)}, this method's
* entry-transformation logic may depend on the key as well as the value.
*
* <p>All other properties of the transformed multimap, such as iteration order, are left intact.
* For example, the code:
*
* <pre>{@code
* Multimap<String, Integer> multimap =
* ImmutableMultimap.of("a", 1, "a", 4, "b", 6);
* EntryTransformer<String, Integer, String> transformer =
* new EntryTransformer<String, Integer, String>() {
* public String transformEntry(String key, Integer value) {
* return key + value;
* }
* };
* Multimap<String, String> transformed =
* Multimaps.transformEntries(multimap, transformer);
* System.out.println(transformed);
* }</pre>
*
* ... prints {@code {"a"=["a1", "a4"], "b"=["b6"]}}.
*
* <p>Changes in the underlying multimap are reflected in this view. Conversely, this view
* supports removal operations, and these are reflected in the underlying multimap.
*
* <p>It's acceptable for the underlying multimap to contain null keys and null values provided
* that the transformer is capable of accepting null inputs. The transformed multimap might
* contain null values if the transformer sometimes gives a null result.
*
* <p>The returned multimap is not thread-safe or serializable, even if the underlying multimap
* is.
*
* <p>The transformer is applied lazily, invoked when needed. This is necessary for the returned
* multimap to be a view, but it means that the transformer will be applied many times for bulk
* operations like {@link Multimap#containsValue} and {@link Object#toString}. For this to perform
* well, {@code transformer} should be fast. To avoid lazy evaluation when the returned multimap
* doesn't need to be a view, copy the returned multimap into a new multimap of your choosing.
*
* <p><b>Warning:</b> This method assumes that for any instance {@code k} of {@code
* EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of
* type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as
* {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the
* transformed multimap.
*
* @since 7.0
*/
public static <K, V1, V2> ListMultimap<K, V2> transformEntries(
ListMultimap<K, V1> fromMap, EntryTransformer<? super K, ? super V1, V2> transformer) {
return new TransformedEntriesListMultimap<>(fromMap, transformer);
}
private static class TransformedEntriesMultimap<K, V1, V2> extends AbstractMultimap<K, V2> {
final Multimap<K, V1> fromMultimap;
final EntryTransformer<? super K, ? super V1, V2> transformer;
TransformedEntriesMultimap(
Multimap<K, V1> fromMultimap,
final EntryTransformer<? super K, ? super V1, V2> transformer) {
this.fromMultimap = checkNotNull(fromMultimap);
this.transformer = checkNotNull(transformer);
}
Collection<V2> transform(K key, Collection<V1> values) {
Function<? super V1, V2> function = Maps.asValueToValueFunction(transformer, key);
if (values instanceof List) {
return Lists.transform((List<V1>) values, function);
} else {
return Collections2.transform(values, function);
}
}
@Override
Map<K, Collection<V2>> createAsMap() {
return Maps.transformEntries(
fromMultimap.asMap(),
new EntryTransformer<K, Collection<V1>, Collection<V2>>() {
@Override
public Collection<V2> transformEntry(K key, Collection<V1> value) {
return transform(key, value);
}
});
}
@Override
public void clear() {
fromMultimap.clear();
}
@Override
public boolean containsKey(Object key) {
return fromMultimap.containsKey(key);
}
@Override
Collection<Entry<K, V2>> createEntries() {
return new Entries();
}
@Override
Iterator<Entry<K, V2>> entryIterator() {
return Iterators.transform(
fromMultimap.entries().iterator(), Maps.<K, V1, V2>asEntryToEntryFunction(transformer));
}
@Override
public Collection<V2> get(final K key) {
return transform(key, fromMultimap.get(key));
}
@Override
public boolean isEmpty() {
return fromMultimap.isEmpty();
}
@Override
Set<K> createKeySet() {
return fromMultimap.keySet();
}
@Override
Multiset<K> createKeys() {
return fromMultimap.keys();
}
@Override
public boolean put(K key, V2 value) {
throw new UnsupportedOperationException();
}
@Override
public boolean putAll(K key, Iterable<? extends V2> values) {
throw new UnsupportedOperationException();
}
@Override
public boolean putAll(Multimap<? extends K, ? extends V2> multimap) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
@Override
public boolean remove(Object key, Object value) {
return get((K) key).remove(value);
}
@SuppressWarnings("unchecked")
@Override
public Collection<V2> removeAll(Object key) {
return transform((K) key, fromMultimap.removeAll(key));
}
@Override
public Collection<V2> replaceValues(K key, Iterable<? extends V2> values) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
return fromMultimap.size();
}
@Override
Collection<V2> createValues() {
return Collections2.transform(
fromMultimap.entries(), Maps.<K, V1, V2>asEntryToValueFunction(transformer));
}
}
private static final class TransformedEntriesListMultimap<K, V1, V2>
extends TransformedEntriesMultimap<K, V1, V2> implements ListMultimap<K, V2> {
TransformedEntriesListMultimap(
ListMultimap<K, V1> fromMultimap, EntryTransformer<? super K, ? super V1, V2> transformer) {
super(fromMultimap, transformer);
}
@Override
List<V2> transform(K key, Collection<V1> values) {
return Lists.transform((List<V1>) values, Maps.asValueToValueFunction(transformer, key));
}
@Override
public List<V2> get(K key) {
return transform(key, fromMultimap.get(key));
}
@SuppressWarnings("unchecked")
@Override
public List<V2> removeAll(Object key) {
return transform((K) key, fromMultimap.removeAll(key));
}
@Override
public List<V2> replaceValues(K key, Iterable<? extends V2> values) {
throw new UnsupportedOperationException();
}
}
Creates an index ImmutableListMultimap
that contains the results of applying a specified function to each item in an Iterable
of values. Each value will be stored as a value in the resulting multimap, yielding a multimap with the same size as the input iterable. The key used to store that value in the multimap will be the result of calling the function on that value. The resulting multimap is created as an immutable snapshot. In the returned multimap, keys appear in the order they are first encountered, and the values corresponding to each key appear in the same order as they are encountered. For example,
List<String> badGuys =
Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Function<String, Integer> stringLengthFunction = ...;
Multimap<Integer, String> index =
Multimaps.index(badGuys, stringLengthFunction);
System.out.println(index);
prints
{4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
The returned multimap is serializable if its keys and values are all serializable.
Params: - values – the values to use when constructing the
ImmutableListMultimap
- keyFunction – the function used to produce the key for each value
Throws: - NullPointerException – if any element of
values
is null
, or if
keyFunction
produces null
for any key
Returns: ImmutableListMultimap
mapping the result of evaluating the function
keyFunction
on each value in the input collection to that value
/**
* Creates an index {@code ImmutableListMultimap} that contains the results of applying a
* specified function to each item in an {@code Iterable} of values. Each value will be stored as
* a value in the resulting multimap, yielding a multimap with the same size as the input
* iterable. The key used to store that value in the multimap will be the result of calling the
* function on that value. The resulting multimap is created as an immutable snapshot. In the
* returned multimap, keys appear in the order they are first encountered, and the values
* corresponding to each key appear in the same order as they are encountered.
*
* <p>For example,
*
* <pre>{@code
* List<String> badGuys =
* Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
* Function<String, Integer> stringLengthFunction = ...;
* Multimap<Integer, String> index =
* Multimaps.index(badGuys, stringLengthFunction);
* System.out.println(index);
* }</pre>
*
* <p>prints
*
* <pre>{@code
* {4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
* }</pre>
*
* <p>The returned multimap is serializable if its keys and values are all serializable.
*
* @param values the values to use when constructing the {@code ImmutableListMultimap}
* @param keyFunction the function used to produce the key for each value
* @return {@code ImmutableListMultimap} mapping the result of evaluating the function {@code
* keyFunction} on each value in the input collection to that value
* @throws NullPointerException if any element of {@code values} is {@code null}, or if {@code
* keyFunction} produces {@code null} for any key
*/
public static <K, V> ImmutableListMultimap<K, V> index(
Iterable<V> values, Function<? super V, K> keyFunction) {
return index(values.iterator(), keyFunction);
}
Creates an index ImmutableListMultimap
that contains the results of applying a specified function to each item in an Iterator
of values. Each value will be stored as a value in the resulting multimap, yielding a multimap with the same size as the input iterator. The key used to store that value in the multimap will be the result of calling the function on that value. The resulting multimap is created as an immutable snapshot. In the returned multimap, keys appear in the order they are first encountered, and the values corresponding to each key appear in the same order as they are encountered. For example,
List<String> badGuys =
Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Function<String, Integer> stringLengthFunction = ...;
Multimap<Integer, String> index =
Multimaps.index(badGuys.iterator(), stringLengthFunction);
System.out.println(index);
prints
{4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
The returned multimap is serializable if its keys and values are all serializable.
Params: - values – the values to use when constructing the
ImmutableListMultimap
- keyFunction – the function used to produce the key for each value
Throws: - NullPointerException – if any element of
values
is null
, or if
keyFunction
produces null
for any key
Returns: ImmutableListMultimap
mapping the result of evaluating the function
keyFunction
on each value in the input collection to that valueSince: 10.0
/**
* Creates an index {@code ImmutableListMultimap} that contains the results of applying a
* specified function to each item in an {@code Iterator} of values. Each value will be stored as
* a value in the resulting multimap, yielding a multimap with the same size as the input
* iterator. The key used to store that value in the multimap will be the result of calling the
* function on that value. The resulting multimap is created as an immutable snapshot. In the
* returned multimap, keys appear in the order they are first encountered, and the values
* corresponding to each key appear in the same order as they are encountered.
*
* <p>For example,
*
* <pre>{@code
* List<String> badGuys =
* Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
* Function<String, Integer> stringLengthFunction = ...;
* Multimap<Integer, String> index =
* Multimaps.index(badGuys.iterator(), stringLengthFunction);
* System.out.println(index);
* }</pre>
*
* <p>prints
*
* <pre>{@code
* {4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
* }</pre>
*
* <p>The returned multimap is serializable if its keys and values are all serializable.
*
* @param values the values to use when constructing the {@code ImmutableListMultimap}
* @param keyFunction the function used to produce the key for each value
* @return {@code ImmutableListMultimap} mapping the result of evaluating the function {@code
* keyFunction} on each value in the input collection to that value
* @throws NullPointerException if any element of {@code values} is {@code null}, or if {@code
* keyFunction} produces {@code null} for any key
* @since 10.0
*/
public static <K, V> ImmutableListMultimap<K, V> index(
Iterator<V> values, Function<? super V, K> keyFunction) {
checkNotNull(keyFunction);
ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder();
while (values.hasNext()) {
V value = values.next();
checkNotNull(value, values);
builder.put(keyFunction.apply(value), value);
}
return builder.build();
}
static class Keys<K, V> extends AbstractMultiset<K> {
@Weak final Multimap<K, V> multimap;
Keys(Multimap<K, V> multimap) {
this.multimap = multimap;
}
@Override
Iterator<Multiset.Entry<K>> entryIterator() {
return new TransformedIterator<Map.Entry<K, Collection<V>>, Multiset.Entry<K>>(
multimap.asMap().entrySet().iterator()) {
@Override
Multiset.Entry<K> transform(final Map.Entry<K, Collection<V>> backingEntry) {
return new Multisets.AbstractEntry<K>() {
@Override
public K getElement() {
return backingEntry.getKey();
}
@Override
public int getCount() {
return backingEntry.getValue().size();
}
};
}
};
}
@Override
public Spliterator<K> spliterator() {
return CollectSpliterators.map(multimap.entries().spliterator(), Map.Entry::getKey);
}
@Override
public void forEach(Consumer<? super K> consumer) {
checkNotNull(consumer);
multimap.entries().forEach(entry -> consumer.accept(entry.getKey()));
}
@Override
int distinctElements() {
return multimap.asMap().size();
}
@Override
public int size() {
return multimap.size();
}
@Override
public boolean contains(@Nullable Object element) {
return multimap.containsKey(element);
}
@Override
public Iterator<K> iterator() {
return Maps.keyIterator(multimap.entries().iterator());
}
@Override
public int count(@Nullable Object element) {
Collection<V> values = Maps.safeGet(multimap.asMap(), element);
return (values == null) ? 0 : values.size();
}
@Override
public int remove(@Nullable Object element, int occurrences) {
checkNonnegative(occurrences, "occurrences");
if (occurrences == 0) {
return count(element);
}
Collection<V> values = Maps.safeGet(multimap.asMap(), element);
if (values == null) {
return 0;
}
int oldCount = values.size();
if (occurrences >= oldCount) {
values.clear();
} else {
Iterator<V> iterator = values.iterator();
for (int i = 0; i < occurrences; i++) {
iterator.next();
iterator.remove();
}
}
return oldCount;
}
@Override
public void clear() {
multimap.clear();
}
@Override
public Set<K> elementSet() {
return multimap.keySet();
}
@Override
Iterator<K> elementIterator() {
throw new AssertionError("should never be called");
}
}
A skeleton implementation of Multimap.entries()
. /** A skeleton implementation of {@link Multimap#entries()}. */
abstract static class Entries<K, V> extends AbstractCollection<Map.Entry<K, V>> {
abstract Multimap<K, V> multimap();
@Override
public int size() {
return multimap().size();
}
@Override
public boolean contains(@Nullable Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
return multimap().containsEntry(entry.getKey(), entry.getValue());
}
return false;
}
@Override
public boolean remove(@Nullable Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
return multimap().remove(entry.getKey(), entry.getValue());
}
return false;
}
@Override
public void clear() {
multimap().clear();
}
}
A skeleton implementation of Multimap.asMap()
. /** A skeleton implementation of {@link Multimap#asMap()}. */
static final class AsMap<K, V> extends Maps.ViewCachingAbstractMap<K, Collection<V>> {
@Weak private final Multimap<K, V> multimap;
AsMap(Multimap<K, V> multimap) {
this.multimap = checkNotNull(multimap);
}
@Override
public int size() {
return multimap.keySet().size();
}
@Override
protected Set<Entry<K, Collection<V>>> createEntrySet() {
return new EntrySet();
}
void removeValuesForKey(Object key) {
multimap.keySet().remove(key);
}
@WeakOuter
class EntrySet extends Maps.EntrySet<K, Collection<V>> {
@Override
Map<K, Collection<V>> map() {
return AsMap.this;
}
@Override
public Iterator<Entry<K, Collection<V>>> iterator() {
return Maps.asMapEntryIterator(
multimap.keySet(),
new Function<K, Collection<V>>() {
@Override
public Collection<V> apply(K key) {
return multimap.get(key);
}
});
}
@Override
public boolean remove(Object o) {
if (!contains(o)) {
return false;
}
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
removeValuesForKey(entry.getKey());
return true;
}
}
@SuppressWarnings("unchecked")
@Override
public Collection<V> get(Object key) {
return containsKey(key) ? multimap.get((K) key) : null;
}
@Override
public Collection<V> remove(Object key) {
return containsKey(key) ? multimap.removeAll(key) : null;
}
@Override
public Set<K> keySet() {
return multimap.keySet();
}
@Override
public boolean isEmpty() {
return multimap.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return multimap.containsKey(key);
}
@Override
public void clear() {
multimap.clear();
}
}
Returns a multimap containing the mappings in unfiltered
whose keys satisfy a predicate. The returned multimap is a live view of unfiltered
; changes to one affect the other. The resulting multimap's views have iterators that don't support remove()
, but all other methods are supported by the multimap and its views. When adding a key that doesn't satisfy the predicate, the multimap's put()
, putAll()
, and
replaceValues()
methods throw an IllegalArgumentException
.
When methods such as removeAll()
and clear()
are called on the filtered multimap or its views, only mappings whose keys satisfy the filter will be removed from the underlying multimap.
The returned multimap isn't threadsafe or serializable, even if unfiltered
is.
Many of the filtered multimap's methods, such as size()
, iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the
copy.
Warning: keyPredicate
must be consistent with equals, as documented at Predicate.apply
. Do not provide a predicate such as
Predicates.instanceOf(ArrayList.class)
, which is inconsistent with equals.
Since: 11.0
/**
* Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a
* predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect
* the other.
*
* <p>The resulting multimap's views have iterators that don't support {@code remove()}, but all
* other methods are supported by the multimap and its views. When adding a key that doesn't
* satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code
* replaceValues()} methods throw an {@link IllegalArgumentException}.
*
* <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
* multimap or its views, only mappings whose keys satisfy the filter will be removed from the
* underlying multimap.
*
* <p>The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is.
*
* <p>Many of the filtered multimap's methods, such as {@code size()}, iterate across every
* key/value mapping in the underlying multimap and determine which satisfy the filter. When a
* live view is <i>not</i> needed, it may be faster to copy the filtered multimap and use the
* copy.
*
* <p><b>Warning:</b> {@code keyPredicate} must be <i>consistent with equals</i>, as documented at
* {@link Predicate#apply}. Do not provide a predicate such as {@code
* Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals.
*
* @since 11.0
*/
public static <K, V> Multimap<K, V> filterKeys(
Multimap<K, V> unfiltered, final Predicate<? super K> keyPredicate) {
if (unfiltered instanceof SetMultimap) {
return filterKeys((SetMultimap<K, V>) unfiltered, keyPredicate);
} else if (unfiltered instanceof ListMultimap) {
return filterKeys((ListMultimap<K, V>) unfiltered, keyPredicate);
} else if (unfiltered instanceof FilteredKeyMultimap) {
FilteredKeyMultimap<K, V> prev = (FilteredKeyMultimap<K, V>) unfiltered;
return new FilteredKeyMultimap<>(
prev.unfiltered, Predicates.<K>and(prev.keyPredicate, keyPredicate));
} else if (unfiltered instanceof FilteredMultimap) {
FilteredMultimap<K, V> prev = (FilteredMultimap<K, V>) unfiltered;
return filterFiltered(prev, Maps.<K>keyPredicateOnEntries(keyPredicate));
} else {
return new FilteredKeyMultimap<>(unfiltered, keyPredicate);
}
}
Returns a multimap containing the mappings in unfiltered
whose keys satisfy a predicate. The returned multimap is a live view of unfiltered
; changes to one affect the other. The resulting multimap's views have iterators that don't support remove()
, but all other methods are supported by the multimap and its views. When adding a key that doesn't satisfy the predicate, the multimap's put()
, putAll()
, and
replaceValues()
methods throw an IllegalArgumentException
.
When methods such as removeAll()
and clear()
are called on the filtered multimap or its views, only mappings whose keys satisfy the filter will be removed from the underlying multimap.
The returned multimap isn't threadsafe or serializable, even if unfiltered
is.
Many of the filtered multimap's methods, such as size()
, iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the
copy.
Warning: keyPredicate
must be consistent with equals, as documented at Predicate.apply
. Do not provide a predicate such as
Predicates.instanceOf(ArrayList.class)
, which is inconsistent with equals.
Since: 14.0
/**
* Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a
* predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect
* the other.
*
* <p>The resulting multimap's views have iterators that don't support {@code remove()}, but all
* other methods are supported by the multimap and its views. When adding a key that doesn't
* satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code
* replaceValues()} methods throw an {@link IllegalArgumentException}.
*
* <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
* multimap or its views, only mappings whose keys satisfy the filter will be removed from the
* underlying multimap.
*
* <p>The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is.
*
* <p>Many of the filtered multimap's methods, such as {@code size()}, iterate across every
* key/value mapping in the underlying multimap and determine which satisfy the filter. When a
* live view is <i>not</i> needed, it may be faster to copy the filtered multimap and use the
* copy.
*
* <p><b>Warning:</b> {@code keyPredicate} must be <i>consistent with equals</i>, as documented at
* {@link Predicate#apply}. Do not provide a predicate such as {@code
* Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals.
*
* @since 14.0
*/
public static <K, V> SetMultimap<K, V> filterKeys(
SetMultimap<K, V> unfiltered, final Predicate<? super K> keyPredicate) {
if (unfiltered instanceof FilteredKeySetMultimap) {
FilteredKeySetMultimap<K, V> prev = (FilteredKeySetMultimap<K, V>) unfiltered;
return new FilteredKeySetMultimap<>(
prev.unfiltered(), Predicates.<K>and(prev.keyPredicate, keyPredicate));
} else if (unfiltered instanceof FilteredSetMultimap) {
FilteredSetMultimap<K, V> prev = (FilteredSetMultimap<K, V>) unfiltered;
return filterFiltered(prev, Maps.<K>keyPredicateOnEntries(keyPredicate));
} else {
return new FilteredKeySetMultimap<>(unfiltered, keyPredicate);
}
}
Returns a multimap containing the mappings in unfiltered
whose keys satisfy a predicate. The returned multimap is a live view of unfiltered
; changes to one affect the other. The resulting multimap's views have iterators that don't support remove()
, but all other methods are supported by the multimap and its views. When adding a key that doesn't satisfy the predicate, the multimap's put()
, putAll()
, and
replaceValues()
methods throw an IllegalArgumentException
.
When methods such as removeAll()
and clear()
are called on the filtered multimap or its views, only mappings whose keys satisfy the filter will be removed from the underlying multimap.
The returned multimap isn't threadsafe or serializable, even if unfiltered
is.
Many of the filtered multimap's methods, such as size()
, iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the
copy.
Warning: keyPredicate
must be consistent with equals, as documented at Predicate.apply
. Do not provide a predicate such as
Predicates.instanceOf(ArrayList.class)
, which is inconsistent with equals.
Since: 14.0
/**
* Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a
* predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect
* the other.
*
* <p>The resulting multimap's views have iterators that don't support {@code remove()}, but all
* other methods are supported by the multimap and its views. When adding a key that doesn't
* satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code
* replaceValues()} methods throw an {@link IllegalArgumentException}.
*
* <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
* multimap or its views, only mappings whose keys satisfy the filter will be removed from the
* underlying multimap.
*
* <p>The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is.
*
* <p>Many of the filtered multimap's methods, such as {@code size()}, iterate across every
* key/value mapping in the underlying multimap and determine which satisfy the filter. When a
* live view is <i>not</i> needed, it may be faster to copy the filtered multimap and use the
* copy.
*
* <p><b>Warning:</b> {@code keyPredicate} must be <i>consistent with equals</i>, as documented at
* {@link Predicate#apply}. Do not provide a predicate such as {@code
* Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals.
*
* @since 14.0
*/
public static <K, V> ListMultimap<K, V> filterKeys(
ListMultimap<K, V> unfiltered, final Predicate<? super K> keyPredicate) {
if (unfiltered instanceof FilteredKeyListMultimap) {
FilteredKeyListMultimap<K, V> prev = (FilteredKeyListMultimap<K, V>) unfiltered;
return new FilteredKeyListMultimap<>(
prev.unfiltered(), Predicates.<K>and(prev.keyPredicate, keyPredicate));
} else {
return new FilteredKeyListMultimap<>(unfiltered, keyPredicate);
}
}
Returns a multimap containing the mappings in unfiltered
whose values satisfy a predicate. The returned multimap is a live view of unfiltered
; changes to one affect the other. The resulting multimap's views have iterators that don't support remove()
, but all other methods are supported by the multimap and its views. When adding a value that doesn't satisfy the predicate, the multimap's put()
, putAll()
, and
replaceValues()
methods throw an IllegalArgumentException
.
When methods such as removeAll()
and clear()
are called on the filtered multimap or its views, only mappings whose value satisfy the filter will be removed from the underlying multimap.
The returned multimap isn't threadsafe or serializable, even if unfiltered
is.
Many of the filtered multimap's methods, such as size()
, iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the
copy.
Warning: valuePredicate
must be consistent with equals, as documented at Predicate.apply
. Do not provide a predicate such as
Predicates.instanceOf(ArrayList.class)
, which is inconsistent with equals.
Since: 11.0
/**
* Returns a multimap containing the mappings in {@code unfiltered} whose values satisfy a
* predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect
* the other.
*
* <p>The resulting multimap's views have iterators that don't support {@code remove()}, but all
* other methods are supported by the multimap and its views. When adding a value that doesn't
* satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code
* replaceValues()} methods throw an {@link IllegalArgumentException}.
*
* <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
* multimap or its views, only mappings whose value satisfy the filter will be removed from the
* underlying multimap.
*
* <p>The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is.
*
* <p>Many of the filtered multimap's methods, such as {@code size()}, iterate across every
* key/value mapping in the underlying multimap and determine which satisfy the filter. When a
* live view is <i>not</i> needed, it may be faster to copy the filtered multimap and use the
* copy.
*
* <p><b>Warning:</b> {@code valuePredicate} must be <i>consistent with equals</i>, as documented
* at {@link Predicate#apply}. Do not provide a predicate such as {@code
* Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals.
*
* @since 11.0
*/
public static <K, V> Multimap<K, V> filterValues(
Multimap<K, V> unfiltered, final Predicate<? super V> valuePredicate) {
return filterEntries(unfiltered, Maps.<V>valuePredicateOnEntries(valuePredicate));
}
Returns a multimap containing the mappings in unfiltered
whose values satisfy a predicate. The returned multimap is a live view of unfiltered
; changes to one affect the other. The resulting multimap's views have iterators that don't support remove()
, but all other methods are supported by the multimap and its views. When adding a value that doesn't satisfy the predicate, the multimap's put()
, putAll()
, and
replaceValues()
methods throw an IllegalArgumentException
.
When methods such as removeAll()
and clear()
are called on the filtered multimap or its views, only mappings whose value satisfy the filter will be removed from the underlying multimap.
The returned multimap isn't threadsafe or serializable, even if unfiltered
is.
Many of the filtered multimap's methods, such as size()
, iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the
copy.
Warning: valuePredicate
must be consistent with equals, as documented at Predicate.apply
. Do not provide a predicate such as
Predicates.instanceOf(ArrayList.class)
, which is inconsistent with equals.
Since: 14.0
/**
* Returns a multimap containing the mappings in {@code unfiltered} whose values satisfy a
* predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect
* the other.
*
* <p>The resulting multimap's views have iterators that don't support {@code remove()}, but all
* other methods are supported by the multimap and its views. When adding a value that doesn't
* satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code
* replaceValues()} methods throw an {@link IllegalArgumentException}.
*
* <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
* multimap or its views, only mappings whose value satisfy the filter will be removed from the
* underlying multimap.
*
* <p>The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is.
*
* <p>Many of the filtered multimap's methods, such as {@code size()}, iterate across every
* key/value mapping in the underlying multimap and determine which satisfy the filter. When a
* live view is <i>not</i> needed, it may be faster to copy the filtered multimap and use the
* copy.
*
* <p><b>Warning:</b> {@code valuePredicate} must be <i>consistent with equals</i>, as documented
* at {@link Predicate#apply}. Do not provide a predicate such as {@code
* Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals.
*
* @since 14.0
*/
public static <K, V> SetMultimap<K, V> filterValues(
SetMultimap<K, V> unfiltered, final Predicate<? super V> valuePredicate) {
return filterEntries(unfiltered, Maps.<V>valuePredicateOnEntries(valuePredicate));
}
Returns a multimap containing the mappings in unfiltered
that satisfy a predicate. The returned multimap is a live view of unfiltered
; changes to one affect the other. The resulting multimap's views have iterators that don't support remove()
, but all other methods are supported by the multimap and its views. When adding a key/value pair that doesn't satisfy the predicate, multimap's put()
, putAll()
, and
replaceValues()
methods throw an IllegalArgumentException
.
When methods such as removeAll()
and clear()
are called on the filtered multimap or its views, only mappings whose keys satisfy the filter will be removed from the underlying multimap.
The returned multimap isn't threadsafe or serializable, even if unfiltered
is.
Many of the filtered multimap's methods, such as size()
, iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the
copy.
Warning: entryPredicate
must be consistent with equals, as documented at Predicate.apply
.
Since: 11.0
/**
* Returns a multimap containing the mappings in {@code unfiltered} that satisfy a predicate. The
* returned multimap is a live view of {@code unfiltered}; changes to one affect the other.
*
* <p>The resulting multimap's views have iterators that don't support {@code remove()}, but all
* other methods are supported by the multimap and its views. When adding a key/value pair that
* doesn't satisfy the predicate, multimap's {@code put()}, {@code putAll()}, and {@code
* replaceValues()} methods throw an {@link IllegalArgumentException}.
*
* <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
* multimap or its views, only mappings whose keys satisfy the filter will be removed from the
* underlying multimap.
*
* <p>The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is.
*
* <p>Many of the filtered multimap's methods, such as {@code size()}, iterate across every
* key/value mapping in the underlying multimap and determine which satisfy the filter. When a
* live view is <i>not</i> needed, it may be faster to copy the filtered multimap and use the
* copy.
*
* <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with equals</i>, as documented
* at {@link Predicate#apply}.
*
* @since 11.0
*/
public static <K, V> Multimap<K, V> filterEntries(
Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) {
checkNotNull(entryPredicate);
if (unfiltered instanceof SetMultimap) {
return filterEntries((SetMultimap<K, V>) unfiltered, entryPredicate);
}
return (unfiltered instanceof FilteredMultimap)
? filterFiltered((FilteredMultimap<K, V>) unfiltered, entryPredicate)
: new FilteredEntryMultimap<K, V>(checkNotNull(unfiltered), entryPredicate);
}
Returns a multimap containing the mappings in unfiltered
that satisfy a predicate. The returned multimap is a live view of unfiltered
; changes to one affect the other. The resulting multimap's views have iterators that don't support remove()
, but all other methods are supported by the multimap and its views. When adding a key/value pair that doesn't satisfy the predicate, multimap's put()
, putAll()
, and
replaceValues()
methods throw an IllegalArgumentException
.
When methods such as removeAll()
and clear()
are called on the filtered multimap or its views, only mappings whose keys satisfy the filter will be removed from the underlying multimap.
The returned multimap isn't threadsafe or serializable, even if unfiltered
is.
Many of the filtered multimap's methods, such as size()
, iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the
copy.
Warning: entryPredicate
must be consistent with equals, as documented at Predicate.apply
.
Since: 14.0
/**
* Returns a multimap containing the mappings in {@code unfiltered} that satisfy a predicate. The
* returned multimap is a live view of {@code unfiltered}; changes to one affect the other.
*
* <p>The resulting multimap's views have iterators that don't support {@code remove()}, but all
* other methods are supported by the multimap and its views. When adding a key/value pair that
* doesn't satisfy the predicate, multimap's {@code put()}, {@code putAll()}, and {@code
* replaceValues()} methods throw an {@link IllegalArgumentException}.
*
* <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
* multimap or its views, only mappings whose keys satisfy the filter will be removed from the
* underlying multimap.
*
* <p>The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is.
*
* <p>Many of the filtered multimap's methods, such as {@code size()}, iterate across every
* key/value mapping in the underlying multimap and determine which satisfy the filter. When a
* live view is <i>not</i> needed, it may be faster to copy the filtered multimap and use the
* copy.
*
* <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with equals</i>, as documented
* at {@link Predicate#apply}.
*
* @since 14.0
*/
public static <K, V> SetMultimap<K, V> filterEntries(
SetMultimap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) {
checkNotNull(entryPredicate);
return (unfiltered instanceof FilteredSetMultimap)
? filterFiltered((FilteredSetMultimap<K, V>) unfiltered, entryPredicate)
: new FilteredEntrySetMultimap<K, V>(checkNotNull(unfiltered), entryPredicate);
}
Support removal operations when filtering a filtered multimap. Since a filtered multimap has
iterators that don't support remove, passing one to the FilteredEntryMultimap constructor would
lead to a multimap whose removal operations would fail. This method combines the predicates to
avoid that problem.
/**
* Support removal operations when filtering a filtered multimap. Since a filtered multimap has
* iterators that don't support remove, passing one to the FilteredEntryMultimap constructor would
* lead to a multimap whose removal operations would fail. This method combines the predicates to
* avoid that problem.
*/
private static <K, V> Multimap<K, V> filterFiltered(
FilteredMultimap<K, V> multimap, Predicate<? super Entry<K, V>> entryPredicate) {
Predicate<Entry<K, V>> predicate =
Predicates.<Entry<K, V>>and(multimap.entryPredicate(), entryPredicate);
return new FilteredEntryMultimap<>(multimap.unfiltered(), predicate);
}
Support removal operations when filtering a filtered multimap. Since a filtered multimap has
iterators that don't support remove, passing one to the FilteredEntryMultimap constructor would
lead to a multimap whose removal operations would fail. This method combines the predicates to
avoid that problem.
/**
* Support removal operations when filtering a filtered multimap. Since a filtered multimap has
* iterators that don't support remove, passing one to the FilteredEntryMultimap constructor would
* lead to a multimap whose removal operations would fail. This method combines the predicates to
* avoid that problem.
*/
private static <K, V> SetMultimap<K, V> filterFiltered(
FilteredSetMultimap<K, V> multimap, Predicate<? super Entry<K, V>> entryPredicate) {
Predicate<Entry<K, V>> predicate =
Predicates.<Entry<K, V>>and(multimap.entryPredicate(), entryPredicate);
return new FilteredEntrySetMultimap<>(multimap.unfiltered(), predicate);
}
static boolean equalsImpl(Multimap<?, ?> multimap, @Nullable Object object) {
if (object == multimap) {
return true;
}
if (object instanceof Multimap) {
Multimap<?, ?> that = (Multimap<?, ?>) object;
return multimap.asMap().equals(that.asMap());
}
return false;
}
// TODO(jlevy): Create methods that filter a SortedSetMultimap.
}