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

There is a newer version: 1.0
Show 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.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Static utility methods pertaining to {@link Map} instances. Also see 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 {@link 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 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 an immutable map for which the {@link Map#values} are the given * elements in the given order, and each key is the product of invoking a * supplied function on its corresponding value. * * @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} is null, or * if {@code keyFunction} produces {@code null} for any value */ // TODO: consider returning a bimap, whose inverse view does lookups by // invoking the function. public static ImmutableMap uniqueIndex( Iterable values, Function keyFunction) { checkNotNull(keyFunction); ImmutableMap.Builder builder = ImmutableMap.builder(); for (V value : values) { builder.put(keyFunction.apply(value), value); } return builder.build(); } /** * 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. The returned map is modifiable and * serializable. * * @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 = 0; } /** * 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 ForwardingCollection> { private final Collection> entries; UnmodifiableEntries(Collection> entries) { this.entries = entries; } @Override protected Collection> delegate() { return entries; } @Override public Iterator> iterator() { final Iterator> delegate = super.iterator(); return new ForwardingIterator>() { @Override public Entry next() { return unmodifiableEntry(super.next()); } @Override protected Iterator> delegate() { return delegate; } }; } // See java.util.Collections.UnmodifiableEntrySet for details on attacks. @Override public Object[] toArray() { return ObjectArrays.toArrayImpl(this); } @Override public T[] toArray(T[] array) { return ObjectArrays.toArrayImpl(this, array); } @Override public boolean contains(Object o) { return containsEntryImpl(delegate(), o); } @Override public boolean containsAll(Collection c) { return Collections2.containsAll(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 Sets.equalsImpl(this, o); } @Override public int hashCode() { return Sets.hashCodeImpl(this); } } /** * Returns a new empty {@code HashBiMap} with the default initial capacity * (16). */ 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 and value * types. * * @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 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, Serializable { final Map unmodifiableMap; final BiMap delegate; transient BiMap inverse; transient Set values; UnmodifiableBiMap(BiMap delegate, BiMap inverse) { unmodifiableMap = Collections.unmodifiableMap(delegate); this.delegate = delegate; this.inverse = inverse; } @Override protected Map delegate() { return unmodifiableMap; } public V forcePut(K key, V value) { throw new UnsupportedOperationException(); } public BiMap inverse() { BiMap result = inverse; return (result == null) ? inverse = new UnmodifiableBiMap(delegate.inverse(), this) : result; } @Override public Set values() { Set result = values; return (result == null) ? values = Collections.unmodifiableSet(delegate.values()) : result; } private static final long serialVersionUID = 0; } /** * 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); } }; 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 = 0; } @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} */ 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 */ static boolean removeEntryImpl(Collection> c, Object o) { if (!(o instanceof Entry)) { return false; } return c.remove(unmodifiableEntry((Entry) o)); } }