| /* |
| * 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 org.checkerframework.checker.nullness.compatqual.MonotonicNonNullDecl; |
| import org.checkerframework.checker.nullness.compatqual.NullableDecl; |
| |
| /** |
| * 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() {} |
| |
| /** |
| * 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 */ |
| @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 {@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 */ |
| @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 {@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 */ |
| @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 {@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 */ |
| @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 {@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 <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 {@code |
| * UnsupportedOperationException}. |
| * |
| * <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 |
| */ |
| @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; |
| @MonotonicNonNullDecl transient Collection<Entry<K, V>> entries; |
| @MonotonicNonNullDecl transient Multiset<K> keys; |
| @MonotonicNonNullDecl transient Set<K> keySet; |
| @MonotonicNonNullDecl transient Collection<V> values; |
| @MonotonicNonNullDecl 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) {@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 {@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>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 |
| */ |
| @Deprecated |
| public static <K, V> SetMultimap<K, V> unmodifiableSetMultimap( |
| ImmutableSetMultimap<K, V> delegate) { |
| return checkNotNull(delegate); |
| } |
| |
| /** |
| * 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 {@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>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) {@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 {@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>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 |
| */ |
| @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 {@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 {@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)); |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| /** |
| * 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 {@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 {@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 {@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 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: |
| * |
| * <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 {@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 {@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 {@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 {@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 {@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 |
| int distinctElements() { |
| return multimap.asMap().size(); |
| } |
| |
| @Override |
| public int size() { |
| return multimap.size(); |
| } |
| |
| @Override |
| public boolean contains(@NullableDecl Object element) { |
| return multimap.containsKey(element); |
| } |
| |
| @Override |
| public Iterator<K> iterator() { |
| return Maps.keyIterator(multimap.entries().iterator()); |
| } |
| |
| @Override |
| public int count(@NullableDecl Object element) { |
| Collection<V> values = Maps.safeGet(multimap.asMap(), element); |
| return (values == null) ? 0 : values.size(); |
| } |
| |
| @Override |
| public int remove(@NullableDecl 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 {@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(@NullableDecl 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(@NullableDecl 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 {@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 {@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 {@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 {@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 {@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 {@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 {@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 {@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. |
| */ |
| 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. |
| */ |
| 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, @NullableDecl 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. |
| } |