| /* |
| * 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 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.NoSuchElementException; |
| import java.util.Set; |
| import java.util.SortedSet; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * Provides static methods acting on or generating a {@code Multimap}. |
| * |
| * <p>See the Guava User Guide article on <a href= |
| * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Multimaps"> |
| * {@code Multimaps}</a>. |
| * |
| * @author Jared Levy |
| * @author Robert Konigsberg |
| * @author Mike Bostock |
| * @author Louis Wasserman |
| * @since 2.0 (imported from Google Collections Library) |
| */ |
| @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}. |
| * |
| * <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<K, V>(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 protected Collection<V> createCollection() { |
| return factory.get(); |
| } |
| |
| // 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<K, V>(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 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<K, V>(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 protected Set<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 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<K, V>(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 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} |
| */ |
| 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>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<K, V>(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; |
| transient Collection<Entry<K, V>> entries; |
| transient Multiset<K> keys; |
| transient Set<K> keySet; |
| transient Collection<V> values; |
| 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>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<K, V>(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>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<K, V>(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>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<K, V>(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<K, V>( |
| 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<K, V>(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 |
| public Set<K> keySet() { |
| return map.keySet(); |
| } |
| |
| @Override |
| public Collection<V> values() { |
| return map.values(); |
| } |
| |
| @Override |
| public Set<Entry<K, V>> entries() { |
| return map.entrySet(); |
| } |
| |
| @Override |
| Iterator<Entry<K, V>> entryIterator() { |
| return map.entrySet().iterator(); |
| } |
| |
| @Override |
| Map<K, Collection<V>> createAsMap() { |
| return new AsMap<K, V>(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 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<K, V1, V2>(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 |
| 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 public Set<K> keySet() { |
| return fromMultimap.keySet(); |
| } |
| |
| @Override public Multiset<K> keys() { |
| 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)); |
| } |
| } |
| |
| /** |
| * 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 {@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<K, V1, V2>(fromMap, 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 of the following cases is true: |
| * <ul> |
| * <li>{@code values} is null |
| * <li>{@code keyFunction} is null |
| * <li>An element in {@code values} is null |
| * <li>{@code keyFunction} returns {@code null} for any element of {@code |
| * values} |
| * </ul> |
| */ |
| 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 of the following cases is true: |
| * <ul> |
| * <li>{@code values} is null |
| * <li>{@code keyFunction} is null |
| * <li>An element in {@code values} is null |
| * <li>{@code keyFunction} returns {@code null} for any element of {@code |
| * values} |
| * </ul> |
| * @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> { |
| 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 Set<Multiset.Entry<K>> createEntrySet() { |
| return new KeysEntrySet(); |
| } |
| |
| class KeysEntrySet extends Multisets.EntrySet<K> { |
| @Override Multiset<K> multiset() { |
| return Keys.this; |
| } |
| |
| @Override public Iterator<Multiset.Entry<K>> iterator() { |
| return entryIterator(); |
| } |
| |
| @Override public int size() { |
| return distinctElements(); |
| } |
| |
| @Override public boolean isEmpty() { |
| return multimap.isEmpty(); |
| } |
| |
| @Override public boolean contains(@Nullable Object o) { |
| if (o instanceof Multiset.Entry) { |
| Multiset.Entry<?> entry = (Multiset.Entry<?>) o; |
| Collection<V> collection = multimap.asMap().get(entry.getElement()); |
| return collection != null && collection.size() == entry.getCount(); |
| } |
| return false; |
| } |
| |
| @Override public boolean remove(@Nullable Object o) { |
| if (o instanceof Multiset.Entry) { |
| Multiset.Entry<?> entry = (Multiset.Entry<?>) o; |
| Collection<V> collection = multimap.asMap().get(entry.getElement()); |
| if (collection != null && collection.size() == entry.getCount()) { |
| collection.clear(); |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| @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(); |
| } |
| } |
| |
| /** |
| * 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 {@link Multimap#asMap()}. |
| */ |
| static final class AsMap<K, V> extends |
| Maps.ImprovedAbstractMap<K, Collection<V>> { |
| 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); |
| } |
| |
| 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<K, V>(prev.unfiltered, |
| Predicates.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<K, V>(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<K, V>(prev.unfiltered(), |
| Predicates.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<K, V>(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<K, V>(prev.unfiltered(), |
| Predicates.and(prev.keyPredicate, keyPredicate)); |
| } else { |
| return new FilteredKeyListMultimap<K, V>(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.and(multimap.entryPredicate(), entryPredicate); |
| return new FilteredEntryMultimap<K, V>(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.and(multimap.entryPredicate(), entryPredicate); |
| return new FilteredEntrySetMultimap<K, V>(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. |
| } |