diff options
author | Yohann Roussel <yroussel@google.com> | 2014-03-19 16:25:37 +0100 |
---|---|---|
committer | Yohann Roussel <yroussel@google.com> | 2014-03-20 15:13:33 +0100 |
commit | 4eceb95409e844fdc33c9c706e1dc307bfd40303 (patch) | |
tree | ee9f4f3fc79f757c79081c336bce4f1782c6ccd8 /guava/src/com/google/common/collect/ImmutableMultimap.java | |
parent | 3d2402901b1a6462e2cf47a6fd09711f327961c3 (diff) | |
download | toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.zip toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.gz toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.bz2 |
Initial Jack import.
Change-Id: I953cf0a520195a7187d791b2885848ad0d5a9b43
Diffstat (limited to 'guava/src/com/google/common/collect/ImmutableMultimap.java')
-rw-r--r-- | guava/src/com/google/common/collect/ImmutableMultimap.java | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/guava/src/com/google/common/collect/ImmutableMultimap.java b/guava/src/com/google/common/collect/ImmutableMultimap.java new file mode 100644 index 0000000..8b6aa29 --- /dev/null +++ b/guava/src/com/google/common/collect/ImmutableMultimap.java @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2008 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 com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * An immutable {@link Multimap}. Does not permit null keys or values. + * + * <p>Unlike {@link Multimaps#unmodifiableMultimap(Multimap)}, which is + * a <i>view</i> of a separate multimap which can still change, an instance of + * {@code ImmutableMultimap} contains its own data and will <i>never</i> + * change. {@code ImmutableMultimap} is convenient for + * {@code public static final} multimaps ("constant multimaps") and also lets + * you easily make a "defensive copy" of a multimap provided to your class by + * a caller. + * + * <p><b>Note:</b> Although this class is not final, it cannot be subclassed as + * it has no public or protected constructors. Thus, instances of this class + * are guaranteed to be immutable. + * + * <p>In addition to methods defined by {@link Multimap}, an {@link #inverse} + * method is also supported. + * + * <p>See the Guava User Guide article on <a href= + * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> + * immutable collections</a>. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public abstract class ImmutableMultimap<K, V> + implements Multimap<K, V>, Serializable { + + /** Returns an empty multimap. */ + public static <K, V> ImmutableMultimap<K, V> of() { + return ImmutableListMultimap.of(); + } + + /** + * Returns an immutable multimap containing a single entry. + */ + public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1) { + return ImmutableListMultimap.of(k1, v1); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1, K k2, V v2) { + return ImmutableListMultimap.of(k1, v1, k2, v2); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static <K, V> ImmutableMultimap<K, V> of( + K k1, V v1, K k2, V v2, K k3, V v3) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static <K, V> ImmutableMultimap<K, V> of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static <K, V> ImmutableMultimap<K, V> of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); + } + + // looking for of() with > 5 entries? Use the builder instead. + + /** + * Returns a new builder. The generated builder is equivalent to the builder + * created by the {@link Builder} constructor. + */ + public static <K, V> Builder<K, V> builder() { + return new Builder<K, V>(); + } + + /** + * Multimap for {@link ImmutableMultimap.Builder} that maintains key and + * value orderings, allows duplicate values, and performs better than + * {@link LinkedListMultimap}. + */ + private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> { + BuilderMultimap() { + super(new LinkedHashMap<K, Collection<V>>()); + } + @Override Collection<V> createCollection() { + return Lists.newArrayList(); + } + private static final long serialVersionUID = 0; + } + + /** + * A builder for creating immutable multimap instances, especially + * {@code public static final} multimaps ("constant multimaps"). Example: + * <pre> {@code + * + * static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP = + * new ImmutableMultimap.Builder<String, Integer>() + * .put("one", 1) + * .putAll("several", 1, 2, 3) + * .putAll("many", 1, 2, 3, 4, 5) + * .build();}</pre> + * + * Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple multimaps in series. Each multimap contains the + * key-value mappings in the previously created multimaps. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static class Builder<K, V> { + Multimap<K, V> builderMultimap = new BuilderMultimap<K, V>(); + Comparator<? super K> keyComparator; + Comparator<? super V> valueComparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableMultimap#builder}. + */ + public Builder() {} + + /** + * Adds a key-value mapping to the built multimap. + */ + public Builder<K, V> put(K key, V value) { + builderMultimap.put(checkNotNull(key), checkNotNull(value)); + return this; + } + + /** + * Adds an entry to the built multimap. + * + * @since 11.0 + */ + public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { + builderMultimap.put( + checkNotNull(entry.getKey()), checkNotNull(entry.getValue())); + return this; + } + + /** + * Stores a collection of values with the same key in the built multimap. + * + * @throws NullPointerException if {@code key}, {@code values}, or any + * element in {@code values} is null. The builder is left in an invalid + * state. + */ + public Builder<K, V> putAll(K key, Iterable<? extends V> values) { + Collection<V> valueList = builderMultimap.get(checkNotNull(key)); + for (V value : values) { + valueList.add(checkNotNull(value)); + } + return this; + } + + /** + * Stores an array of values with the same key in the built multimap. + * + * @throws NullPointerException if the key or any value is null. The builder + * is left in an invalid state. + */ + public Builder<K, V> putAll(K key, V... values) { + return putAll(key, Arrays.asList(values)); + } + + /** + * Stores another multimap's entries in the built multimap. The generated + * multimap's key and value orderings correspond to the iteration ordering + * of the {@code multimap.asMap()} view, with new keys and values following + * any existing keys and values. + * + * @throws NullPointerException if any key or value in {@code multimap} is + * null. The builder is left in an invalid state. + */ + public Builder<K, V> putAll(Multimap<? extends K, ? extends V> multimap) { + for (Entry<? extends K, ? extends Collection<? extends V>> entry + : multimap.asMap().entrySet()) { + putAll(entry.getKey(), entry.getValue()); + } + return this; + } + + /** + * Specifies the ordering of the generated multimap's keys. + * + * @since 8.0 + */ + @Beta + public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { + this.keyComparator = checkNotNull(keyComparator); + return this; + } + + /** + * Specifies the ordering of the generated multimap's values for each key. + * + * @since 8.0 + */ + @Beta + public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { + this.valueComparator = checkNotNull(valueComparator); + return this; + } + + /** + * Returns a newly-created immutable multimap. + */ + public ImmutableMultimap<K, V> build() { + if (valueComparator != null) { + for (Collection<V> values : builderMultimap.asMap().values()) { + List<V> list = (List <V>) values; + Collections.sort(list, valueComparator); + } + } + if (keyComparator != null) { + Multimap<K, V> sortedCopy = new BuilderMultimap<K, V>(); + List<Map.Entry<K, Collection<V>>> entries = Lists.newArrayList( + builderMultimap.asMap().entrySet()); + Collections.sort( + entries, + Ordering.from(keyComparator).onResultOf(new Function<Entry<K, Collection<V>>, K>() { + @Override + public K apply(Entry<K, Collection<V>> entry) { + return entry.getKey(); + } + })); + for (Map.Entry<K, Collection<V>> entry : entries) { + sortedCopy.putAll(entry.getKey(), entry.getValue()); + } + builderMultimap = sortedCopy; + } + return copyOf(builderMultimap); + } + } + + /** + * Returns an immutable multimap containing the same mappings as {@code + * multimap}. The generated multimap's key and value orderings correspond to + * the iteration ordering of the {@code multimap.asMap()} view. + * + * <p>Despite the method name, this method attempts to avoid actually copying + * the data when it is safe to do so. The exact circumstances under which a + * copy will or will not be performed are undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code multimap} is + * null + */ + public static <K, V> ImmutableMultimap<K, V> copyOf( + Multimap<? extends K, ? extends V> multimap) { + if (multimap instanceof ImmutableMultimap) { + @SuppressWarnings("unchecked") // safe since multimap is not writable + ImmutableMultimap<K, V> kvMultimap + = (ImmutableMultimap<K, V>) multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + return ImmutableListMultimap.copyOf(multimap); + } + + final transient ImmutableMap<K, ? extends ImmutableCollection<V>> map; + final transient int size; + + // These constants allow the deserialization code to set final fields. This + // holder class makes sure they are not initialized unless an instance is + // deserialized. + @GwtIncompatible("java serialization is not supported") + static class FieldSettersHolder { + static final Serialization.FieldSetter<ImmutableMultimap> + MAP_FIELD_SETTER = Serialization.getFieldSetter( + ImmutableMultimap.class, "map"); + static final Serialization.FieldSetter<ImmutableMultimap> + SIZE_FIELD_SETTER = Serialization.getFieldSetter( + ImmutableMultimap.class, "size"); + } + + ImmutableMultimap(ImmutableMap<K, ? extends ImmutableCollection<V>> map, + int size) { + this.map = map; + this.size = size; + } + + // mutators (not supported) + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public ImmutableCollection<V> removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public ImmutableCollection<V> replaceValues(K key, + Iterable<? extends V> values) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Returns an immutable collection of the values for the given key. If no + * mappings in the multimap have the provided key, an empty immutable + * collection is returned. The values are in the same order as the parameters + * used to build this multimap. + */ + @Override + public abstract ImmutableCollection<V> get(K key); + + /** + * Returns an immutable multimap which is the inverse of this one. For every + * key-value mapping in the original, the result will have a mapping with + * key and value reversed. + * + * @since 11.0 + */ + @Beta + public abstract ImmutableMultimap<V, K> inverse(); + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public boolean putAll(K key, Iterable<? extends V> values) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public boolean putAll(Multimap<? extends K, ? extends V> multimap) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + */ + @Override + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + boolean isPartialView() { + return map.isPartialView(); + } + + // accessors + + @Override + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { + Collection<V> values = map.get(key); + return values != null && values.contains(value); + } + + @Override + public boolean containsKey(@Nullable Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(@Nullable Object value) { + for (Collection<V> valueCollection : map.values()) { + if (valueCollection.contains(value)) { + return true; + } + } + return false; + } + + @Override + public boolean isEmpty() { + return size == 0; + } + + @Override + public int size() { + return size; + } + + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Multimap) { + Multimap<?, ?> that = (Multimap<?, ?>) object; + return this.map.equals(that.asMap()); + } + return false; + } + + @Override public int hashCode() { + return map.hashCode(); + } + + @Override public String toString() { + return map.toString(); + } + + // views + + /** + * Returns an immutable set of the distinct keys in this multimap. These keys + * are ordered according to when they first appeared during the construction + * of this multimap. + */ + @Override + public ImmutableSet<K> keySet() { + return map.keySet(); + } + + /** + * Returns an immutable map that associates each key with its corresponding + * values in the multimap. + */ + @Override + @SuppressWarnings("unchecked") // a widening cast + public ImmutableMap<K, Collection<V>> asMap() { + return (ImmutableMap) map; + } + + private transient ImmutableCollection<Entry<K, V>> entries; + + /** + * Returns an immutable collection of all key-value pairs in the multimap. Its + * iterator traverses the values for the first key, the values for the second + * key, and so on. + */ + @Override + public ImmutableCollection<Entry<K, V>> entries() { + ImmutableCollection<Entry<K, V>> result = entries; + return (result == null) + ? (entries = new EntryCollection<K, V>(this)) : result; + } + + private static class EntryCollection<K, V> + extends ImmutableCollection<Entry<K, V>> { + final ImmutableMultimap<K, V> multimap; + + EntryCollection(ImmutableMultimap<K, V> multimap) { + this.multimap = multimap; + } + + @Override public UnmodifiableIterator<Entry<K, V>> iterator() { + final Iterator<? extends Entry<K, ? extends ImmutableCollection<V>>> + mapIterator = this.multimap.map.entrySet().iterator(); + + return new UnmodifiableIterator<Entry<K, V>>() { + K key; + Iterator<V> valueIterator; + + @Override + public boolean hasNext() { + return (key != null && valueIterator.hasNext()) + || mapIterator.hasNext(); + } + + @Override + public Entry<K, V> next() { + if (key == null || !valueIterator.hasNext()) { + Entry<K, ? extends ImmutableCollection<V>> entry + = mapIterator.next(); + key = entry.getKey(); + valueIterator = entry.getValue().iterator(); + } + return Maps.immutableEntry(key, valueIterator.next()); + } + }; + } + + @Override boolean isPartialView() { + return multimap.isPartialView(); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override public boolean contains(Object object) { + if (object instanceof Entry) { + Entry<?, ?> entry = (Entry<?, ?>) object; + return multimap.containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + private static final long serialVersionUID = 0; + } + + private transient ImmutableMultiset<K> keys; + + /** + * Returns a collection, which may contain duplicates, of all keys. The number + * of times a key appears in the returned multiset equals the number of + * mappings the key has in the multimap. Duplicate keys appear consecutively + * in the multiset's iteration order. + */ + @Override + public ImmutableMultiset<K> keys() { + ImmutableMultiset<K> result = keys; + return (result == null) ? (keys = createKeys()) : result; + } + + private ImmutableMultiset<K> createKeys() { + return new Keys(); + } + + @SuppressWarnings("serial") // Uses writeReplace, not default serialization + class Keys extends ImmutableMultiset<K> { + @Override + public boolean contains(@Nullable Object object) { + return containsKey(object); + } + + @Override + public int count(@Nullable Object element) { + Collection<V> values = map.get(element); + return (values == null) ? 0 : values.size(); + } + + @Override + public Set<K> elementSet() { + return keySet(); + } + + @Override + public int size() { + return ImmutableMultimap.this.size(); + } + + @Override + ImmutableSet<Entry<K>> createEntrySet() { + return new KeysEntrySet(); + } + + private class KeysEntrySet extends ImmutableMultiset<K>.EntrySet { + @Override + public int size() { + return keySet().size(); + } + + @Override + public UnmodifiableIterator<Entry<K>> iterator() { + return asList().iterator(); + } + + @Override + ImmutableList<Entry<K>> createAsList() { + final ImmutableList<? extends Map.Entry<K, ? extends Collection<V>>> mapEntries = + map.entrySet().asList(); + return new ImmutableAsList<Entry<K>>() { + @Override + public Entry<K> get(int index) { + Map.Entry<K, ? extends Collection<V>> entry = mapEntries.get(index); + return Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); + } + + @Override + ImmutableCollection<Entry<K>> delegateCollection() { + return KeysEntrySet.this; + } + }; + } + } + + @Override + boolean isPartialView() { + return true; + } + } + + private transient ImmutableCollection<V> values; + + /** + * Returns an immutable collection of the values in this multimap. Its + * iterator traverses the values for the first key, the values for the second + * key, and so on. + */ + @Override + public ImmutableCollection<V> values() { + ImmutableCollection<V> result = values; + return (result == null) ? (values = new Values<V>(this)) : result; + } + + private static class Values<V> extends ImmutableCollection<V> { + final ImmutableMultimap<?, V> multimap; + + Values(ImmutableMultimap<?, V> multimap) { + this.multimap = multimap; + } + + @Override public UnmodifiableIterator<V> iterator() { + return Maps.valueIterator(multimap.entries().iterator()); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override boolean isPartialView() { + return true; + } + + private static final long serialVersionUID = 0; + } + + private static final long serialVersionUID = 0; +} |