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

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

Go to download

Google Collections Library is a suite of new collections and collection-related goodness for Java 5.0

The newest version!
/*
 * 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.Function;
import com.google.common.base.Nullable;
import com.google.common.base.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.MapConstraints.ConstrainedMap;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Provides static methods for creating mutable {@code Map} instances easily.
 * You can replace code like:
 *
 * 

{@code Map map = new HashMap();} * *

with just: * *

{@code Map map = Maps.newHashMap();} * *

Supported today are: {@link HashMap}, {@link LinkedHashMap}, {@link * ConcurrentHashMap}, {@link TreeMap}, and {@link EnumMap}. * *

See also this class's counterparts {@link Lists} and {@link Sets}. * * @author Kevin Bourrillion * @author Mike Bostock */ public final class Maps { private Maps() {} /** * Creates a {@code HashMap} instance. * *

Note: if {@code K} is an {@code enum} type, use {@link * #newEnumMap} instead. * *

Note: if you don't actually need the resulting map to be mutable, * use {@link Collections#emptyMap} instead. * * @return a newly-created, initially-empty {@code HashMap} */ public static HashMap newHashMap() { return new HashMap(); } /** * Creates a {@code HashMap} instance with enough capacity to hold the * specified number of elements without rehashing. * * @param expectedSize the expected size * @return a newly-created {@code HashMap}, initially empty, with enough * capacity to hold {@code expectedSize} elements without rehashing * @throws IllegalArgumentException if {@code expectedSize} is negative */ public static HashMap newHashMapWithExpectedSize( int expectedSize) { /* * The HashMap is constructed with an initialCapacity that's greater than * expectedSize. The larger value is necessary because HashMap resizes * its internal array if the map size exceeds loadFactor * initialCapacity. */ return new HashMap(capacity(expectedSize)); } /** * Returns an appropriate value for the "capacity" (in reality, "minimum * table size") parameter of a HashMap constructor, such that the resulting * table will be between 25% and 50% full when it contains * {@code expectedSize} entries. * * @throws IllegalArgumentException if {@code expectedSize} is negative */ static int capacity(int expectedSize) { checkArgument(expectedSize >= 0); return Math.max(expectedSize * 2, 16); } /** * Creates a {@code HashMap} instance with the same mappings as the specified * map. * *

Note: if {@code K} is an {@link Enum} type, use {@link * #newEnumMap} instead. * * @param map the mappings to be placed in the new map * @return a newly-created {@code HashMap} initialized with the mappings from * {@code map} */ public static HashMap newHashMap(Map map) { return new HashMap(map); } /** * Creates an insertion-ordered {@code LinkedHashMap} instance. * * @return a newly-created, initially-empty {@code LinkedHashMap} */ public static LinkedHashMap newLinkedHashMap() { return new LinkedHashMap(); } /** * Creates an insertion-ordered {@code LinkedHashMap} instance with the same * mappings as the specified map. * * @param map the mappings to be placed in the new map * @return a newly-created, {@code LinkedHashMap} initialized with the * mappings from {@code map} */ public static LinkedHashMap newLinkedHashMap(Map map) { return new LinkedHashMap(map); } /** * Creates a {@code ConcurrentHashMap} instance. * * @return a newly-created, initially-empty {@code ConcurrentHashMap} */ public static ConcurrentHashMap newConcurrentHashMap() { return new ConcurrentHashMap(); } /** * Creates a {@code TreeMap} instance using the natural ordering of its * elements. * * @return a newly-created, initially-empty {@code TreeMap} */ @SuppressWarnings("unchecked") // allow ungenerified Comparable types public static TreeMap newTreeMap() { return new TreeMap(); } /** * Creates a {@code TreeMap} instance using the given comparator. * * @param comparator the Comparator to sort the keys with * @return a newly-created, initially-empty {@code TreeMap} */ public static TreeMap newTreeMap( @Nullable Comparator comparator) { // Ideally, the extra type parameter "C" shouldn't be necessary. It is a // work-around of a compiler type inference quirk that prevents the // following code from being compiled: // Comparator> comparator = null; // Map, String> map = newTreeMap(comparator); return new TreeMap(comparator); } /** * Creates an {@code EnumMap} instance. * * @param type the key type for this map * @return a newly-created, initially-empty {@code EnumMap} */ public static , V> EnumMap newEnumMap(Class type) { return new EnumMap(type); } /** * Creates an {@code IdentityHashMap} instance. * * @return a newly-created, initially-empty {@code IdentityHashMap} */ public static IdentityHashMap newIdentityHashMap() { return new IdentityHashMap(); } /** * Returns {@code true} if {@code map} contains an entry mapping {@code key} * to {@code value}. If you are not concerned with null-safety you can simply * use {@code map.get(key).equals(value)}. */ public static boolean containsEntry( Map map, @Nullable Object key, @Nullable Object value) { Object valueForKey = map.get(key); return (valueForKey == null) ? value == null && map.containsKey(key) : valueForKey.equals(value); } /** * Returns an immutable empty {@code BiMap} instance. */ @SuppressWarnings("unchecked") public static BiMap immutableBiMap() { return (BiMap) EMPTY_BIMAP; } private static final BiMap EMPTY_BIMAP = new EmptyBiMap(); private static class EmptyBiMap extends ForwardingMap implements BiMap { public EmptyBiMap() { super(ImmutableMap.of()); } @Override public Set values() { return Collections.emptySet(); } public Object forcePut(Object key, Object value) { throw new UnsupportedOperationException(); } public BiMap inverse() { return this; } private Object readResolve() { return EMPTY_BIMAP; // preserve singleton property } private static final long serialVersionUID = 0; } /** * Creates a new immutable {@code BiMap} instance containing the given * key-value pair. */ public static BiMap immutableBiMap( @Nullable K k1, @Nullable V v1) { return new ImmutableBiMapBuilder() .put(k1, v1) .getBiMap(); } /** * Creates a new immutable {@code BiMap} instance containing the given * key-value pairs. * * @see ImmutableBiMapBuilder */ public static BiMap immutableBiMap( @Nullable K k1, @Nullable V v1, @Nullable K k2, @Nullable V v2) { return new ImmutableBiMapBuilder() .put(k1, v1) .put(k2, v2) .getBiMap(); } /** * Creates a new immutable {@code BiMap} instance containing the given * key-value pairs. * * @see ImmutableBiMapBuilder */ public static BiMap immutableBiMap( @Nullable K k1, @Nullable V v1, @Nullable K k2, @Nullable V v2, @Nullable K k3, @Nullable V v3) { return new ImmutableBiMapBuilder() .put(k1, v1) .put(k2, v2) .put(k3, v3) .getBiMap(); } /** * Creates a new immutable {@code BiMap} instance containing the given * key-value pairs. * * @see ImmutableBiMapBuilder */ public static BiMap immutableBiMap( @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 ImmutableBiMapBuilder() .put(k1, v1) .put(k2, v2) .put(k3, v3) .put(k4, v4) .getBiMap(); } /** * Creates a new immutable {@code BiMap} instance containing the given * key-value pairs. * * @see ImmutableBiMapBuilder */ public static BiMap immutableBiMap( @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 ImmutableBiMapBuilder() .put(k1, v1) .put(k2, v2) .put(k3, v3) .put(k4, v4) .put(k5, v5) .getBiMap(); } /* * Please use ImmutableBiMapBuilder directly if you are looking for overloads * for 6 or more key-value pairs. */ /** * Returns a synchronized (thread-safe) bimap backed by the specified bimap. * In order to guarantee serial access, it is critical that all access * to the backing bimap is accomplished through the returned bimap. * *

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

  Bimap<K,V> m = Maps.synchronizedBiMap(
   *      new HashBiMap<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 bimap the bimap to be wrapped in a synchronized view * @return a sychronized view of the specified bimap */ public static BiMap synchronizedBiMap(BiMap bimap) { return Synchronized.biMap(bimap, null); } /** * Returns a sorted set view of the keys contained in the specified map. The * set is backed by the map, so changes to the map are reflected in the set, * and vice versa. If the map is modified while an iteration over the set is * in progress (except through the iterator's own {@code remove} operation), * the results of the iteration are undefined. The set supports element * removal, which removes the corresponding mapping from the map, via the * {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, {@code * removeAll}, and {@code clear} operations. It does not support the add or * {@code addAll} operations. * * @return a sorted set view of the keys contained in the specified map. */ public static SortedSet sortedKeySet(SortedMap map) { return new SortedMapKeySet(map); } private static class SortedMapKeySet extends NonSerializableForwardingSet implements SortedSet { final SortedMap map; SortedMapKeySet(SortedMap map) { super(map.keySet()); this.map = map; } public Comparator comparator() { return map.comparator(); } public K first() { return map.firstKey(); } public SortedSet headSet(K toElement) { return new SortedMapKeySet(map.headMap(toElement)); } public K last() { return map.lastKey(); } public SortedSet subSet(K fromElement, K toElement) { return new SortedMapKeySet(map.subMap(fromElement, toElement)); } public SortedSet tailSet(K fromElement) { return new SortedMapKeySet(map.tailMap(fromElement)); } } /** * Creates an index {@code Map} 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 map. The key used to store * that value in the map will be the result of calling the function on that * value. Neither keys nor values are allowed to be null. It is an error if * the function produces the same key for more than one value in the input * collection. * * @param values the values to use when constructing the {@code Map} * @param keyFunction the function used to produce the key for each value * @return a map mapping the result of evaluating the function {@code * keyFunction} on each value in the input collection to that value * @throws IllegalArgumentException if {@code keyFunction} produces the same * key for more than one value in the input collection * @throws NullPointerException if any elements of {@code values} are null, or * if {@code keyFunction} produces {@code null} for any value */ public static Map uniqueIndex(Iterable values, Function keyFunction) { HashMap newMap; if (values instanceof Collection) { // If it's really a collection, take advantage of knowing the size @SuppressWarnings("unchecked") Collection collection = (Collection) values; newMap = new HashMap(collection.size()); } else { newMap = new HashMap(); } return uniqueIndex(newMap, values.iterator(), keyFunction); } /** * Creates an index {@code Map} that contains the results of applying a * specified function to each item in a {@code Collection} of values. Each * value will be stored as a value in the resulting map. The key used to store * that value in the map will be the result of calling the function on that * value. Neither keys nor values are allowed to be null. It is an error if * the function produces the same key for more than one value in the input * collection. * * @param values the values to use when constructing the {@code Map} * @param keyFunction the function used to produce the key for each value * @return {@code Map} mapping the result of evaluating the function {@code * keyFunction} on each value in the input collection to that value * @throws IllegalArgumentException if {@code keyFunction} produces the same * key for more than one value in the input collection * @throws NullPointerException if any elements of {@code values} are null, or * if {@code keyFunction} produces {@code null} for any value */ public static Map uniqueIndex(Collection values, Function keyFunction) { return uniqueIndex(new HashMap(capacity(values.size())), values.iterator(), keyFunction); } /** * Creates an index {@code Map} 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 map. The key used to store * that value in the map will be the result of calling the function on that * value. Neither keys nor values are allowed to be null. It is an error if * the function produces the same key for more than one value in the input * collection. * * @param values the values to use when constructing the {@code Map} * @param keyFunction the function used to produce the key for each value * @return {@code Map} mapping the result of evaluating the function {@code * keyFunction} on each value in the input collection to that value * @throws IllegalArgumentException if {@code keyFunction} produces the same * key for more than one value in the input collection * @throws NullPointerException if any elements of {@code values} are null, or * if {@code keyFunction} produces {@code null} for any value */ public static Map uniqueIndex(Iterator values, Function keyFunction) { return uniqueIndex(new HashMap(), values, keyFunction); } private static Map uniqueIndex( Map map, Iterator values, Function keyFunction) { checkNotNull(keyFunction); while (values.hasNext()) { V value = checkNotNull(values.next(), "null index values not allowed"); K key = checkNotNull(keyFunction.apply(value), "null index keys not allowed"); checkArgument(map.put(key, value) == null, "Duplicate key: %s", key); } return map; } /** * Creates a {@code Map} from a {@code Properties} instance. * Properties normally derive from {@code Map}, but they * typically contain strings, which is awkward. This method lets you get a * plain-old-{@code Map} out of a {@code Properties}. The returned map won't * include any null keys or values. * * @param properties a {@code Properties} object to be converted * @return a map containing all the entries in {@code properties} */ public static Map fromProperties(Properties properties) { Map ret = newHashMapWithExpectedSize(properties.size()); for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) { Object k = e.nextElement(); /* * It is unlikely that a 'null' could be inserted into a Properties, but * possible in a derived class. */ String key = (k != null) ? k.toString() : null; ret.put(key, properties.getProperty(key)); } return ret; } /** * Returns an immutable map entry with the specified key and value. The {@link * Entry#setValue} operation throws an {@link UnsupportedOperationException}. * *

The returned entry is serializable. * * @param key the key to be associated with the returned entry * @param value the value to be associated with the returned entry */ public static Entry immutableEntry( @Nullable final K key, @Nullable final V value) { return new ImmutableEntry(key, value); } /** @see Maps#immutableEntry(Object,Object) */ private static class ImmutableEntry extends AbstractMapEntry implements Serializable { final K key; final V value; ImmutableEntry(K key, V value) { this.key = key; this.value = value; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } private static final long serialVersionUID = 8715539841043489689L; } /** * Returns an unmodifiable view of the specified set of entries. The {@link * Entry#setValue} operation throws an {@link UnsupportedOperationException}, * as do any operations that would modify the returned set. * * @param entrySet the entries for which to return an unmodifiable view * @return an unmodifiable view of the entries */ static Set> unmodifiableEntrySet( final Set> entrySet) { return new UnmodifiableEntrySet(Collections.unmodifiableSet( entrySet)); } /** * Returns an unmodifiable view of the specified map entry. The {@link * Entry#setValue} operation throws an {@link UnsupportedOperationException}. * This also has the side-effect of redefining {@code equals} to comply with * the Entry contract, 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 Entry unmodifiableEntry(final Entry entry) { checkNotNull(entry); return new AbstractMapEntry() { @Override public K getKey() { return entry.getKey(); } @Override public V getValue() { return entry.getValue(); } }; } /** @see Multimaps#unmodifiableEntries */ static class UnmodifiableEntries extends NonSerializableForwardingCollection> { UnmodifiableEntries(Collection> entries) { super(entries); } @Override public Iterator> iterator() { return new ForwardingIterator>(super.iterator()) { @Override public Entry next() { return unmodifiableEntry(super.next()); } }; } // See java.util.Collections.UnmodifiableEntrySet for details on attacks. @Override public Object[] toArray() { return ForwardingCollection.toArrayImpl(this); } @Override public T[] toArray(T[] array) { return ForwardingCollection.toArrayImpl(this, array); } @Override public boolean contains(Object o) { return containsEntryImpl(delegate(), o); } @Override public boolean containsAll(Collection c) { return ForwardingCollection.containsAllImpl(this, c); } } /** @see Maps#unmodifiableEntrySet(Set) */ static class UnmodifiableEntrySet extends UnmodifiableEntries implements Set> { UnmodifiableEntrySet(Set> entries) { super(entries); } // See java.util.Collections.UnmodifiableEntrySet for details on attacks. @Override public boolean equals(Object o) { return ForwardingSet.equalsImpl(this, o); } } /** * Returns a new empty {@code HashBiMap} with the default initial capacity * (16) and load factor (0.75). */ public static HashBiMap newHashBiMap() { return new HashBiMap(); } /** * Returns a new empty {@code EnumHashBiMap} using the specified key type. * * @param keyType the key type */ public static , V> EnumHashBiMap newEnumHashBiMap( Class keyType) { return new EnumHashBiMap(keyType); } /** * Returns a new empty {@code EnumBiMap} using the specified key type and * value type. * * @param keyType the key type * @param valueType the value type */ public static , V extends Enum> EnumBiMap newEnumBiMap(Class keyType, Class valueType) { return new EnumBiMap(keyType, valueType); } /** * Returns a new {@code BiMap}, backed by the two supplied empty maps. The * caller surrenders control of these maps and should not retain any * references to either. * *

The returned bimap will be serializable if both of the specified maps * are serializable. * * @param forward an empty map to be used for associating keys with values * @param backward an empty map to be used for associating values with keys * @throws IllegalArgumentException if either map is nonempty */ public static BiMap newBiMap( Map forward, Map backward) { return new StandardBiMap(forward, backward); } /** * Returns an unmodifiable view of the specified bimap. This method allows * modules to provide users with "read-only" access to internal bimaps. Query * operations on the returned bimap "read through" to the specified bimap, and * attemps to modify the returned map, whether direct or via its collection * views, result in an {@code UnsupportedOperationException}. * *

The returned bimap will be serializable if the specified bimap is * serializable. * * @param bimap the bimap for which an unmodifiable view is to be returned * @return an unmodifiable view of the specified bimap */ public static BiMap unmodifiableBiMap(BiMap bimap) { return new UnmodifiableBiMap(bimap, null); } /** @see Maps#unmodifiableBiMap(BiMap) */ private static class UnmodifiableBiMap extends ForwardingMap implements BiMap { final BiMap delegate; transient volatile BiMap inverse; UnmodifiableBiMap(BiMap delegate, BiMap inverse) { super(Collections.unmodifiableMap(delegate)); this.delegate = delegate; this.inverse = inverse; } public V forcePut(K key, V value) { throw new UnsupportedOperationException(); } public BiMap inverse() { if (inverse == null) { inverse = new UnmodifiableBiMap(delegate.inverse(), this); } return inverse; } @Override public Set values() { return Collections.unmodifiableSet(delegate.values()); } private static final long serialVersionUID = 9106827410356097381L; } /** * Returns a new {@code ClassToInstanceMap} instance backed by a {@link * HashMap} using the default initial capacity and load factor. */ public static ClassToInstanceMap newClassToInstanceMap() { return newClassToInstanceMap(new HashMap, B>()); } /** * Returns a new {@code ClassToInstanceMap} instance backed by a given empty * {@code backingMap}. The caller surrenders control of the backing map, and * thus should not allow any direct references to it to remain accessible. */ public static ClassToInstanceMap newClassToInstanceMap( Map, B> backingMap) { return new SimpleClassToInstanceMap(backingMap); } private static final MapConstraint, Object> VALUE_CAN_BE_CAST_TO_KEY = new MapConstraint, Object>() { public void checkKeyValue(Class key, Object value) { wrap(key).cast(value); } }; // TODO: should be a public final class like the rest, right? private static class SimpleClassToInstanceMap extends ConstrainedMap, B> implements ClassToInstanceMap { SimpleClassToInstanceMap(Map, B> delegate) { super(delegate, VALUE_CAN_BE_CAST_TO_KEY); } public T putInstance(Class type, T value) { B oldValue = put(type, value); return wrap(type).cast(oldValue); } public T getInstance(Class type) { B value = get(type); return wrap(type).cast(value); } private static final long serialVersionUID = 3549975116715378971L; } @SuppressWarnings("unchecked") private static Class wrap(Class c) { return c.isPrimitive() ? (Class) PRIMITIVES_TO_WRAPPERS.get(c) : c; } private static final Map, Class> PRIMITIVES_TO_WRAPPERS = new ImmutableMap.Builder, Class>() .put(boolean.class, Boolean.class) .put(byte.class, Byte.class) .put(char.class, Character.class) .put(double.class, Double.class) .put(float.class, Float.class) .put(int.class, Integer.class) .put(long.class, Long.class) .put(short.class, Short.class) .put(void.class, Void.class) .build(); /** * Implements {@code Collection.contains} safely for forwarding collections of * map entries. If {@code o} is an instance of {@code Map.Entry}, it is * wrapped using {@link #unmodifiableEntry} to protect against a possible * nefarious equals method. * *

Note that {@code c} is the backing (delegate) collection, rather than * the forwarding collection. * * @param c the delegate (unwrapped) collection of map entries * @param o the object that might be contained in {@code c} * @return {@code true} if {@code c} contains {@code o} */ @SuppressWarnings("unchecked") static boolean containsEntryImpl(Collection> c, Object o) { if (!(o instanceof Entry)) { return false; } return c.contains(unmodifiableEntry((Entry) o)); } /** * Implements {@code Collection.remove} safely for forwarding collections of * map entries. If {@code o} is an instance of {@code Map.Entry}, it is * wrapped using {@link #unmodifiableEntry} to protect against a possible * nefarious equals method. * *

Note that {@code c} is backing (delegate) collection, rather than the * forwarding collection. * * @param c the delegate (unwrapped) collection of map entries * @param o the object to remove from {@code c} * @return {@code true} if {@code c} was changed */ @SuppressWarnings("unchecked") static boolean removeEntryImpl(Collection> c, Object o) { if (!(o instanceof Entry)) { return false; } return c.remove(unmodifiableEntry((Entry) o)); } }