All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.Multimaps Maven / Gradle / Ivy

/*
 * Copyright (C) 2007 Google Inc.
 *
 * 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 com.google.common.base.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Supplier;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
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;

/**
 * Provides static methods acting on or generating a {@code Multimap}.
 *
 * @author Jared Levy
 * @author Robert Konigsberg
 * @author Mike Bostock
 */
public final class Multimaps {
  private static final ListMultimap EMPTY_LIST_MULTIMAP
      = new ImmutableMultimapBuilder().getMultimap();

  private Multimaps() {}

  /**
   * Creates an empty {@code HashMultimap} instance.
   *
   * @return a newly-created, initially-empty {@code HashMultimap}
   */
  public static  HashMultimap newHashMultimap() {
    return new HashMultimap();
  }

  /**
   * Creates a {@code HashMultimap} instance initialized with all elements from
   * the supplied {@code Multimap}. If the supplied multimap supports duplicate
   * key-value pairs, those duplicate pairs will only be stored once in the new
   * multimap.
   *
   * @param multimap the multimap whose contents are copied to this multimap.
   * @return a newly-created and initialized {@code HashMultimap}
   */
  public static  HashMultimap newHashMultimap(
      Multimap multimap) {
    return new HashMultimap(multimap);
  }

  /**
   * Creates an empty {@code ArrayListMultimap} instance.
   *
   * @return a newly-created, initially-empty {@code ArrayListMultimap}
   */
  public static  ArrayListMultimap newArrayListMultimap() {
    return new ArrayListMultimap();
  }

  /**
   * Creates an {@code ArrayListMultimap} instance initialized with all elements
   * from the supplied {@code Multimap}.
   *
   * @param multimap the multimap whose contents are copied to this multimap.
   * @return a newly-created and initialized {@code ArrayListMultimap}
   */
  public static  ArrayListMultimap newArrayListMultimap(
      Multimap multimap) {
    return new ArrayListMultimap(multimap);
  }

  /**
   * Creates an empty {@code LinkedHashMultimap} instance.
   *
   * @return a newly-created, initially-empty {@code LinkedHashMultimap}
   */
  public static  LinkedHashMultimap newLinkedHashMultimap() {
    return new LinkedHashMultimap();
  }

  /**
   * Creates a {@code LinkedHashMultimap} instance initialized with all elements
   * from the supplied {@code Multimap}. The ordering follows {@link
   * Multimap#entries()}. If the supplied multimap supports duplicate key-value
   * those duplicate pairs will only be stored once in the new multimap.
   *
   * @param multimap the multimap whose contents are copied to this multimap.
   * @return a newly-created and initialized {@code LinkedHashMultimap}
   */
  public static  LinkedHashMultimap newLinkedHashMultimap(
      Multimap multimap) {
    return new LinkedHashMultimap(multimap);
  }

  /**
   * Creates an empty {@code LinkedListMultimap} instance.
   *
   * @return a newly-created, initially-empty {@code LinkedListMultimap}
   */
  public static  LinkedListMultimap newLinkedListMultimap() {
    return new LinkedListMultimap();
  }

  /**
   * Creates a {@code LinkedListMultimap} instance initialized with all elements
   * from the supplied {@code Multimap}.
   *
   * @param multimap the multimap whose contents are copied to this multimap.
   * @return a newly-created and initialized {@code LinkedListMultimap}
   */
  public static  LinkedListMultimap newLinkedListMultimap(
      Multimap multimap) {
    return new LinkedListMultimap(multimap);
  }

  /**
   * Creates an empty {@code TreeMultimap} instance using the natural ordering
   * of keys and values. If the supplied multimap supports duplicate key-value
   * pairs, those duplicate pairs will only be stored once in the new multimap.
   *
   * @return a newly-created, initially-empty {@code TreeMultimap}
   */
  @SuppressWarnings("unchecked")  // allow ungenerified Comparable types
  public static 
      TreeMultimap newTreeMultimap() {
    return new TreeMultimap();
  }

  /**
   * Constructs a {@code TreeMultimap} with the same mappings as the specified
   * {@code Multimap}.
   *
   * 

If the supplied multimap is an instance of TreeMultimap, then the * supplied multimap's comparators are copied to the new instance. * *

If the supplied multimap is not an instance of TreeMultimap, the new * multimap is ordered using the natural ordering of the key and value * classes. The key and value classes must satisfy the {@link Comparable} * interface. * * @param multimap the multimap whose contents are copied to this multimap. * @return a newly-created and initialized {@code TreeMultimap} */ public static TreeMultimap newTreeMultimap( Multimap multimap) { return new TreeMultimap(multimap); } /** * Creates an empty {@code TreeMultimap} instance using explicit comparators. * * @param keyComparator the comparator that determines the key ordering. If * it's {@code null}, the natural ordering of the keys is used. * @param valueComparator the comparator that determines the value ordering. * If it's {@code null}, the natural ordering of the values is used. * @return a newly-created, initially-empty {@code TreeMultimap} */ public static TreeMultimap newTreeMultimap( @Nullable Comparator keyComparator, @Nullable Comparator valueComparator) { return new TreeMultimap(keyComparator, valueComparator); } /** * Creates a {@code TreeMultimap} instance using explicit comparators, * initialized with all elements from the supplied {@code Multimap}. * * @param multimap the multimap whose contents are copied to this multimap. * @return a newly-created and initialized {@code TreeMultimap} */ public static TreeMultimap newTreeMultimap( @Nullable Comparator keyComparator, @Nullable Comparator valueComparator, Multimap multimap) { return new TreeMultimap(keyComparator, valueComparator, multimap); } /** * Creates a new {@code Multimap} that uses the provided map and factory. It * can generate a multimap based on arbitrary {@link Map} and * {@link Collection} classes. * *

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 multimaps's {@code get} * method returns instances of a different class than {@code factory.get()} * does. * *

Call this method only when the simpler methods * {@link #newArrayListMultimap()}, {@link #newHashMultimap()}, * {@link #newLinkedHashMultimap()}, {@link #newLinkedListMultimap()}, and * {@link #newTreeMultimap()} won't suffice. * * @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 Multimap newMultimap(Map> map, final Supplier> factory) { return new StandardMultimap(map) { @Override protected Collection createCollection() { return factory.get(); } }; } /** * 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. * *

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 multimaps's {@code get} * method returns instances of a different class than {@code factory.get()} * does. * *

Call this method only when the simpler methods * {@link #newArrayListMultimap()} and {@link #newLinkedListMultimap()} won't * suffice. * * @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 ListMultimap newListMultimap( Map> map, final Supplier> factory) { return new StandardListMultimap(map) { @Override protected List createCollection() { return factory.get(); } }; } /** * 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. * *

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 multimaps's {@code get} * method returns instances of a different class than {@code factory.get()} * does. * *

Call this method only when the simpler methods * {@link #newHashMultimap()}, {@link #newLinkedHashMultimap()}, and * {@link #newTreeMultimap()} won't suffice. * * @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 SetMultimap newSetMultimap( Map> map, final Supplier> factory) { return new StandardSetMultimap(map) { @Override protected Set createCollection() { return factory.get(); } }; } /** * 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. * *

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 multimaps's {@code get} * method returns instances of a different class than {@code factory.get()} * does. * *

Call this method only when the simpler method {@link #newTreeMultimap()} * won't suffice. * * @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 SortedSetMultimap newSortedSetMultimap( Map> map, final Supplier> factory) { return new StandardSortedSetMultimap(map) { @Override protected SortedSet createCollection() { return factory.get(); } }; } /** * Creates a {@code HashMultimap} that's the inverse of the provided multimap. * If the input multimap includes the mapping from a key to a value, the * returned multimap contains a mapping from the value to the key. * *

If the input multimap has duplicate key-value mappings, the returned * multimap includes the inverse mapping once. * *

The returned multimap is modifiable. Updating it will not affect the * input multimap, and vice versa. * * @param multimap the multimap to invert * @return the inverse of the input multimap */ public static HashMultimap inverseHashMultimap( Multimap multimap) { HashMultimap inverse = new HashMultimap(); addInverse(inverse, multimap); return inverse; } /** * Creates an {@code ArrayListMultimap} that's the inverse of the provided * multimap. If the input multimap includes the mapping from a key to a value, * the returned multimap contains a mapping from the value to the key. * *

The returned multimap is modifiable. Updating it will not affect the * input multimap, and vice versa. * * @param multimap the multimap to invert * @return the inverse of the input multimap */ public static ArrayListMultimap inverseArrayListMultimap( Multimap multimap) { ArrayListMultimap inverse = new ArrayListMultimap(); addInverse(inverse, multimap); return inverse; } /** * Creates a {@code LinkedHashMultimap} that's the inverse of the provided * multimap. If the input multimap includes the mapping from a key to a value, * the returned multimap contains a mapping from the value to the key. * *

The iteration order of the input multi map determines the sequence in * which data is added to the returned multimap. See {@link * LinkedHashMultimap} for information about the resulting iteration ordering. * *

If the input multimap has duplicate key-value mappings, the returned * multimap includes the inverse mapping once. * *

The returned multimap is modifiable. Updating it will not affect the * input multimap, and vice versa. * * @param multimap the multimap to invert * @return the inverse of the input multimap */ public static LinkedHashMultimap inverseLinkedHashMultimap( Multimap multimap) { LinkedHashMultimap inverse = new LinkedHashMultimap(); addInverse(inverse, multimap); return inverse; } /** * Creates a {@code TreeMultimap} that's the inverse of the provided multimap. * If the input multimap includes the mapping from a key to a value, the * returned multimap contains a mapping from the value to the key. * *

If the input multimap has duplicate key-value mappings, the returned * multimap includes the inverse mapping once. * *

The returned multimap is modifiable. Updating it will not affect the * input map, and vice versa. The returned multimap orders the keys and values * according to their natural ordering. * * @param multimap the multimap to invert * @return the inverse of the input multimap */ public static TreeMultimap inverseTreeMultimap( Multimap multimap) { TreeMultimap inverse = new TreeMultimap(); addInverse(inverse, multimap); return inverse; } /** * Creates a {@code LinkedListMultimap} that's the inverse of the provided * multimap. If the input multimap includes the mapping from a key to a value, * the returned multimap contains a mapping from the value to the key. * *

The returned multimap is modifiable. Updating it will not affect the * input multimap, and vice versa. * * @param multimap the multimap to invert * @return the inverse of the input multimap */ public static LinkedListMultimap inverseLinkedListMultimap( Multimap multimap) { LinkedListMultimap inverse = new LinkedListMultimap(); addInverse(inverse, multimap); return inverse; } /** Inverts a multimap's entries and puts them in the inverse multimap. */ private static void addInverse(Multimap inverse, Multimap multimap) { for (Map.Entry entry : multimap.entries()) { inverse.put(entry.getValue(), entry.getKey()); } } /** * Returns a synchronized (thread-safe) multimap backed by the specified * multimap. In order to guarantee serial access, it is critical that * all access to the backing multimap is accomplished through the * returned multimap. * *

It is imperative that the user manually synchronize on the returned map * when accessing any of its collection views: * *

  Multimap<K,V> m = Multimaps.synchronizedMultimap(
   *      new HashMultimap<K,V>());
   *   ...
   *  Set<K> s = m.keySet();  // Needn't be in synchronized block
   *   ...
   *  synchronized (m) {  // Synchronizing on m, not s!
   *    Iterator<K> i = s.iterator(); // Must be in synchronized block
   *    while (i.hasNext()) {
   *      foo(i.next());
   *    }
   *  }
* * Failure to follow this advice may result in non-deterministic behavior. * * @param multimap the multimap to be wrapped in a synchronized view * @return a sychronized view of the specified multimap */ public static Multimap synchronizedMultimap( Multimap 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, whether direct or via the various * collections returned through operations, result in an {@code * UnsupportedOperationException}. * * @param delegate the multimap for which an unmodifiable view is to be * returned * @return an unmodifiable view of the specified multimap */ public static Multimap unmodifiableMultimap( Multimap delegate) { return new UnmodifiableMultimap(delegate); } private static final class UnmodifiableMultimap extends ForwardingMultimap { transient volatile Collection> entries; transient volatile Multiset keys; transient volatile Set keySet; transient volatile Collection values; transient volatile Map> map; public UnmodifiableMultimap(final Multimap delegate) { super(delegate); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public Map> asMap() { if (map == null) { final Map> unmodifiableMap = Collections.unmodifiableMap(delegate().asMap()); map = new ForwardingMap>(unmodifiableMap) { @SuppressWarnings("hiding") volatile Collection> values; volatile Set>> entrySet; @Override public Set>> entrySet() { if (entrySet == null) { entrySet = unmodifiableAsMapEntries(super.entrySet()); } return entrySet; } @Override public Collection get(Object key) { Collection collection = super.get(key); return (collection == null) ? null : unmodifiableValueCollection(collection); } @Override public Collection> values() { if (values == null) { values = new UnmodifiableAsMapValues(super.values()); } return values; } @Override public boolean containsValue(Object o) { return values().contains(o); } }; } return map; } @Override public Collection> entries() { if (entries == null) { entries = unmodifiableEntries(super.entries()); } return entries; } @Override public Collection get(K key) { return unmodifiableValueCollection(super.get(key)); } @Override public Multiset keys() { if (keys == null) { keys = Multisets.unmodifiableMultiset(super.keys()); } return keys; } @Override public Set keySet() { if (keySet == null) { keySet = Collections.unmodifiableSet(super.keySet()); } return keySet; } @Override @SuppressWarnings("unused") public boolean put(K key, V value) { throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unused") public void putAll(K key, @SuppressWarnings("hiding") Iterable values) { throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unused") public void putAll(Multimap multimap) { throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unused") public boolean remove(Object key, Object value) { throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unused") public Collection removeAll(Object key) { throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unused") public Collection replaceValues(K key, @SuppressWarnings("hiding") Iterable values) { throw new UnsupportedOperationException(); } @Override public Collection values() { if (values == null) { values = Collections.unmodifiableCollection(super.values()); } return values; } } private static class UnmodifiableAsMapValues extends ForwardingCollection> { UnmodifiableAsMapValues(Collection> delegate) { super(Collections.unmodifiableCollection(delegate)); } @Override public Iterator> iterator() { final Iterator> iterator = super.iterator(); return new Iterator>() { public boolean hasNext() { return iterator.hasNext(); } public Collection next() { return unmodifiableValueCollection(iterator.next()); } public void remove() { throw new UnsupportedOperationException(); } }; } @Override public Object[] toArray() { return toArrayImpl(this); } @Override public T[] toArray(T[] array) { return toArrayImpl(this, array); } @Override public boolean contains(Object o) { return containsImpl(this, o); } @Override public boolean containsAll(Collection c) { return containsAllImpl(this, c); } } /** * Returns a synchronized (thread-safe) {@code SetMultimap} backed by the * specified multimap. * *

You must follow the warning described for {@link #synchronizedMultimap}. * * @param multimap the multimap to be wrapped * @return a sychronized view of the specified multimap */ public static SetMultimap synchronizedSetMultimap( SetMultimap multimap) { return new SetDecorator(synchronizedMultimap(multimap)); } /** * {@code SetMultimap} generated by casting the collections returned by the * provided multimap. */ static class SetDecorator extends ForwardingMultimap implements SetMultimap { SetDecorator(Multimap delegate) { super(delegate); } @Override public Set get(@Nullable K key) { return (Set) super.get(key); } @Override public Set> entries() { return (Set>) super.entries(); } @Override public Set removeAll(@Nullable Object key) { return (Set) super.removeAll(key); } @Override public Set replaceValues( @Nullable K key, Iterable values) { return (Set) super.replaceValues(key, values); } private static final long serialVersionUID = -6403585629821552555L; } /** * 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, whether direct or * via the various collections returned through operations, result in an * {@code UnsupportedOperationException}. * * @param delegate the multimap for which an unmodifiable view is to be * returned * @return an unmodifiable view of the specified multimap */ public static SetMultimap unmodifiableSetMultimap( SetMultimap delegate) { return new SetDecorator(unmodifiableMultimap(delegate)); } /** * Returns a synchronized (thread-safe) {@code SortedSetMultimap} backed by * the specified multimap. * *

You must follow the warning described for {@link * #synchronizedMultimap}. * * @param multimap the multimap to be wrapped * @return a sychronized view of the specified multimap */ public static SortedSetMultimap synchronizedSortedSetMultimap(SortedSetMultimap multimap) { return new SortedSetDecorator(synchronizedMultimap(multimap)); } /** * {@code SortedSetMultimap} generated by casting the collections returned by * the provided multimap. */ static class SortedSetDecorator extends ForwardingMultimap implements SortedSetMultimap { SortedSetDecorator(Multimap delegate) { super(delegate); } @Override public SortedSet get(@Nullable K key) { return (SortedSet) super.get(key); } @Override public Set> entries() { return (Set>) super.entries(); } @Override public SortedSet removeAll(@Nullable Object key) { return (SortedSet) super.removeAll(key); } @Override public SortedSet replaceValues( @Nullable K key, Iterable values) { return (SortedSet) super.replaceValues(key, values); } private static final long serialVersionUID = 7463737759006994232L; } /** * 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, whether direct or * via the various collections returned through operations, result in an * {@code UnsupportedOperationException}. * * @param delegate the multimap for which an unmodifiable view is to be * returned * @return an unmodifiable view of the specified multimap */ public static SortedSetMultimap unmodifiableSortedSetMultimap( SortedSetMultimap delegate) { return new SortedSetDecorator(unmodifiableMultimap(delegate)); } /** * Returns a synchronized (thread-safe) {@code ListMultimap} backed by the * specified multimap. * *

You must follow the warning described for {@link #synchronizedMultimap}. * * @param multimap the multimap to be wrapped * @return a sychronized view of the specified multimap */ public static ListMultimap synchronizedListMultimap( ListMultimap multimap) { return new ListDecorator(synchronizedMultimap(multimap)); } /** * {@code ListMultimap} generated by casting the collections returned by the * provided multimap. */ static class ListDecorator extends ForwardingMultimap implements ListMultimap { ListDecorator(Multimap delegate) { super(delegate); } @Override public List get(@Nullable K key) { return (List) super.get(key); } @Override public List removeAll(@Nullable Object key) { return (List) super.removeAll(key); } @Override public List replaceValues( @Nullable K key, Iterable values) { return (List) super.replaceValues(key, values); } private static final long serialVersionUID = -8065289457161999256L; } /** * 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, whether direct or * via the various collections returned through operations, result in an * {@code UnsupportedOperationException}. * * @param delegate the multimap for which an unmodifiable view is to be * returned * @return an unmodifiable view of the specified multimap */ public static ListMultimap unmodifiableListMultimap( ListMultimap delegate) { return new ListDecorator(unmodifiableMultimap(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 Collection unmodifiableValueCollection( Collection collection) { if (collection instanceof SortedSet) { return Collections.unmodifiableSortedSet((SortedSet) collection); } else if (collection instanceof Set) { return Collections.unmodifiableSet((Set) collection); } else if (collection instanceof List) { return Collections.unmodifiableList((List) collection); } return Collections.unmodifiableCollection(collection); } /** * Returns an unmodifiable view of the specified multimap {@code asMap} entry. * The {@link Entry#setValue} operation throws an {@link * UnsupportedOperationException}, and the collection returned by {@code * getValue} is also an unmodifiable (type-preserving) view. This also has the * side-effect of redefining equals to comply with the Map.Entry contract, and * to avoid a possible nefarious implementation of equals. * * @param entry the entry for which to return an unmodifiable view * @return an unmodifiable view of the entry */ private static Map.Entry> unmodifiableAsMapEntry( final Map.Entry> entry) { checkNotNull(entry); return new AbstractMapEntry>() { @Override public K getKey() { return entry.getKey(); } @Override public Collection getValue() { return unmodifiableValueCollection(entry.getValue()); } }; } /** * 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 Collection> unmodifiableEntries( Collection> entries) { if (entries instanceof Set) { return Maps.unmodifiableEntrySet((Set>) entries); } return new Maps.UnmodifiableEntries( Collections.unmodifiableCollection(entries)); } /** * Returns an unmodifiable view of the specified set of {@code asMap} entries. * The {@link Entry#setValue} operation throws an {@link * UnsupportedOperationException}, as do any operations that attempt to modify * the returned collection. * * @param asMapEntries the {@code asMap} entries for which to return an * unmodifiable view * @return an unmodifiable view of the collection entries */ private static Set>> unmodifiableAsMapEntries( Set>> asMapEntries) { return new UnmodifiableAsMapEntries( Collections.unmodifiableSet(asMapEntries)); } /** @see Multimaps#unmodifiableAsMapEntries */ static class UnmodifiableAsMapEntries extends ForwardingSet>> { UnmodifiableAsMapEntries( Set>> asMapEntries) { super(asMapEntries); } @Override public Iterator>> iterator() { return new ForwardingIterator>>(super.iterator()) { @Override public Entry> next() { return unmodifiableAsMapEntry(super.next()); } }; } @Override public Object[] toArray() { return toArrayImpl(this); } @Override public T[] toArray(T[] array) { return toArrayImpl(this, array); } @Override public boolean contains(Object o) { return Maps.containsEntryImpl(delegate(), o); } @Override public boolean containsAll(Collection c) { return containsAllImpl(this, c); } @Override public boolean equals(Object o) { return equalsImpl(this, o); } } /** * 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. * *

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}. * *

The returned multimap will be serializable if the specified map is * serializable. * * @param map the backing map for the returned multimap view */ public static SetMultimap forMap(Map map) { return new MapMultimap(map); } /** @see Multimaps#forMap */ private static class MapMultimap implements SetMultimap, Serializable { final Map map; transient volatile Map> asMap; MapMultimap(Map map) { this.map = checkNotNull(map); } public int size() { return map.size(); } public boolean isEmpty() { return map.isEmpty(); } public boolean containsKey(Object key) { return map.containsKey(key); } public boolean containsValue(Object value) { return map.containsValue(value); } public boolean containsEntry(Object key, Object value) { return map.entrySet().contains(Maps.immutableEntry(key, value)); } public Set get(final K key) { return new AbstractSet() { @Override public Iterator iterator() { return new Iterator() { int i; public boolean hasNext() { return (i == 0) && map.containsKey(key); } public V next() { if (!hasNext()) { throw new NoSuchElementException(); } i++; return map.get(key); } public void remove() { checkState(i == 1); i = -1; map.remove(key); } }; } @Override public int size() { return map.containsKey(key) ? 1 : 0; } }; } public boolean put(K key, V value) { throw new UnsupportedOperationException(); } public void putAll(K key, Iterable values) { throw new UnsupportedOperationException(); } public void putAll(Multimap map) { throw new UnsupportedOperationException(); } public Set replaceValues(K key, Iterable values) { throw new UnsupportedOperationException(); } public boolean remove(Object key, Object value) { return map.entrySet().remove(Maps.immutableEntry(key, value)); } public Set removeAll(Object key) { Set values = new HashSet(2, 0.75f); if (!map.containsKey(key)) { return values; } values.add(map.remove(key)); return values; } public void clear() { map.clear(); } public Set keySet() { return map.keySet(); } public Multiset keys() { return Multisets.forSet(map.keySet()); } public Collection values() { return map.values(); } public Set> entries() { return map.entrySet(); } public Map> asMap() { if (asMap == null) { asMap = new AsMap(); } return asMap; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Multimap)) { return false; } Multimap m = (Multimap) o; if (map.size() != m.size()) { return false; } for (Entry e : map.entrySet()) { if (!m.containsEntry(e.getKey(), e.getValue())) { return false; } } return true; } @Override public int hashCode() { return map.hashCode(); } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append('{'); for (Entry e : map.entrySet()) { buf.append(e.getKey()).append("=[").append(e.getValue()).append("], "); } if (buf.length() > 1) { buf.setLength(buf.length() - 2); // delete last comma and space } buf.append('}'); return buf.toString(); } /** @see MapMultimap#asMap */ class AsMapEntries extends AbstractSet>> { @Override public int size() { return map.size(); } @Override public Iterator>> iterator() { return new Iterator>>() { final Iterator keys = map.keySet().iterator(); public boolean hasNext() { return keys.hasNext(); } public Entry> next() { final K key = keys.next(); return new AbstractMapEntry>() { @Override public K getKey() { return key; } @Override public Collection getValue() { return get(key); } }; } public void remove() { keys.remove(); } }; } // TODO: faster contains, remove? } /** @see MapMultimap#asMap */ class AsMap extends AbstractMap> { private volatile Set>> entries; @Override public Set>> entrySet() { if (entries == null) { entries = new AsMapEntries(); } return entries; } // The following methods are included for performance. @Override public boolean containsKey(Object key) { return map.containsKey(key); } @SuppressWarnings("unchecked") @Override public Collection get(Object key) { Collection collection = MapMultimap.this.get((K) key); return collection.isEmpty() ? null : collection; } @Override public Collection remove(Object key) { Collection collection = removeAll(key); return collection.isEmpty() ? null : collection; } } private static final long serialVersionUID = 7845222491160860175L; } /** * Returns the immutable empty multimap. */ @SuppressWarnings("unchecked") public static ListMultimap immutableMultimap() { return (ListMultimap) EMPTY_LIST_MULTIMAP; } /** * Returns a new immutable multimap containing the specified key-value pair. */ public static ListMultimap immutableMultimap( @Nullable K k1, @Nullable V v1) { return new ImmutableMultimapBuilder().put(k1, v1).getMultimap(); } /** * Returns a new immutable multimap containing the specified key-value pairs. * TODO: nuke this? * *

Unlike an unmodifiable multimap such as that returned by {@link * Multimaps#unmodifiableMultimap}, which provides a read-only view of an * underlying multimap which may itself be mutable, an immutable * multimap makes a copy of the original mappings, so that the returned * multimap is guaranteed never to change. This is critical, for * example, if the multimap is an element of a {@code HashSet} or a key in a * {@code HashMap}. * * @see ImmutableMapBuilder */ public static ListMultimap immutableMultimap( @Nullable K k1, @Nullable V v1, @Nullable K k2, @Nullable V v2) { return new ImmutableMultimapBuilder() .put(k1, v1) .put(k2, v2) .getMultimap(); } /** * Returns a new immutable multimap containing the specified key-value pairs. * TODO: nuke this? * *

Unlike an unmodifiable multimap such as that returned by {@link * Multimaps#unmodifiableMultimap}, which provides a read-only view of an * underlying multimap which may itself be mutable, an immutable * multimap makes a copy of the original mappings, so that the returned * multimap is guaranteed never to change. This is critical, for * example, if the multimap is an element of a {@code HashSet} or a key in a * {@code HashMap}. * * @see ImmutableMapBuilder */ public static ListMultimap immutableMultimap( @Nullable K k1, @Nullable V v1, @Nullable K k2, @Nullable V v2, @Nullable K k3, @Nullable V v3) { return new ImmutableMultimapBuilder() .put(k1, v1) .put(k2, v2) .put(k3, v3) .getMultimap(); } /** * Returns a new immutable multimap containing the specified key-value pairs. * TODO: nuke this? * *

Unlike an unmodifiable multimap such as that returned by {@link * Multimaps#unmodifiableMultimap}, which provides a read-only view of an * underlying multimap which may itself be mutable, an immutable * multimap makes a copy of the original mappings, so that the returned * multimap is guaranteed never to change. This is critical, for * example, if the multimap is an element of a {@code HashSet} or a key in a * {@code HashMap}. * * @see ImmutableMapBuilder */ public static ListMultimap immutableMultimap( @Nullable K k1, @Nullable V v1, @Nullable K k2, @Nullable V v2, @Nullable K k3, @Nullable V v3, @Nullable K k4, @Nullable V v4) { return new ImmutableMultimapBuilder() .put(k1, v1) .put(k2, v2) .put(k3, v3) .put(k4, v4) .getMultimap(); } /** * Returns a new immutable multimap containing the specified key-value pairs. * TODO: nuke this? * *

Unlike an unmodifiable multimap such as that returned by {@link * Multimaps#unmodifiableMultimap}, which provides a read-only view of an * underlying multimap which may itself be mutable, an immutable * multimap makes a copy of the original mappings, so that the returned * multimap is guaranteed never to change. This is critical, for * example, if the multimap is an element of a {@code HashSet} or a key in a * {@code HashMap}. * * @see ImmutableMapBuilder */ public static ListMultimap immutableMultimap( @Nullable K k1, @Nullable V v1, @Nullable K k2, @Nullable V v2, @Nullable K k3, @Nullable V v3, @Nullable K k4, @Nullable V v4, @Nullable K k5, @Nullable V v5) { return new ImmutableMultimapBuilder() .put(k1, v1) .put(k2, v2) .put(k3, v3) .put(k4, v4) .put(k5, v5) .getMultimap(); } /* * Please use ImmutableMultimapBuilder directly if you are looking for * overloads for 6 or more key-value pairs. */ }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy