Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2009 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import static com.google.common.base.Preconditions.checkNotNull;
     20 import static java.util.Arrays.asList;
     21 
     22 import com.google.common.annotations.GwtCompatible;
     23 import com.google.common.annotations.GwtIncompatible;
     24 import com.google.common.base.MoreObjects;
     25 
     26 import java.io.IOException;
     27 import java.io.InvalidObjectException;
     28 import java.io.ObjectInputStream;
     29 import java.io.ObjectOutputStream;
     30 import java.util.Arrays;
     31 import java.util.Collection;
     32 import java.util.Collections;
     33 import java.util.Comparator;
     34 import java.util.LinkedHashMap;
     35 import java.util.List;
     36 import java.util.Map;
     37 import java.util.Map.Entry;
     38 
     39 import javax.annotation.Nullable;
     40 
     41 /**
     42  * An immutable {@link SetMultimap} with reliable user-specified key and value
     43  * iteration order. Does not permit null keys or values.
     44  *
     45  * <p>Unlike {@link Multimaps#unmodifiableSetMultimap(SetMultimap)}, which is
     46  * a <i>view</i> of a separate multimap which can still change, an instance of
     47  * {@code ImmutableSetMultimap} contains its own data and will <i>never</i>
     48  * change. {@code ImmutableSetMultimap} is convenient for
     49  * {@code public static final} multimaps ("constant multimaps") and also lets
     50  * you easily make a "defensive copy" of a multimap provided to your class by
     51  * a caller.
     52  *
     53  * <p><b>Note:</b> Although this class is not final, it cannot be subclassed as
     54  * it has no public or protected constructors. Thus, instances of this class
     55  * are guaranteed to be immutable.
     56  *
     57  * <p>See the Guava User Guide article on <a href=
     58  * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
     59  * immutable collections</a>.
     60  *
     61  * @author Mike Ward
     62  * @since 2.0 (imported from Google Collections Library)
     63  */
     64 @GwtCompatible(serializable = true, emulated = true)
     65 public class ImmutableSetMultimap<K, V>
     66     extends ImmutableMultimap<K, V>
     67     implements SetMultimap<K, V> {
     68 
     69   /** Returns the empty multimap. */
     70   // Casting is safe because the multimap will never hold any elements.
     71   @SuppressWarnings("unchecked")
     72   public static <K, V> ImmutableSetMultimap<K, V> of() {
     73     return (ImmutableSetMultimap<K, V>) EmptyImmutableSetMultimap.INSTANCE;
     74   }
     75 
     76   /**
     77    * Returns an immutable multimap containing a single entry.
     78    */
     79   public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1) {
     80     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
     81     builder.put(k1, v1);
     82     return builder.build();
     83   }
     84 
     85   /**
     86    * Returns an immutable multimap containing the given entries, in order.
     87    * Repeated occurrences of an entry (according to {@link Object#equals}) after
     88    * the first are ignored.
     89    */
     90   public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1, K k2, V v2) {
     91     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
     92     builder.put(k1, v1);
     93     builder.put(k2, v2);
     94     return builder.build();
     95   }
     96 
     97   /**
     98    * Returns an immutable multimap containing the given entries, in order.
     99    * Repeated occurrences of an entry (according to {@link Object#equals}) after
    100    * the first are ignored.
    101    */
    102   public static <K, V> ImmutableSetMultimap<K, V> of(
    103       K k1, V v1, K k2, V v2, K k3, V v3) {
    104     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
    105     builder.put(k1, v1);
    106     builder.put(k2, v2);
    107     builder.put(k3, v3);
    108     return builder.build();
    109   }
    110 
    111   /**
    112    * Returns an immutable multimap containing the given entries, in order.
    113    * Repeated occurrences of an entry (according to {@link Object#equals}) after
    114    * the first are ignored.
    115    */
    116   public static <K, V> ImmutableSetMultimap<K, V> of(
    117       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
    118     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
    119     builder.put(k1, v1);
    120     builder.put(k2, v2);
    121     builder.put(k3, v3);
    122     builder.put(k4, v4);
    123     return builder.build();
    124   }
    125 
    126   /**
    127    * Returns an immutable multimap containing the given entries, in order.
    128    * Repeated occurrences of an entry (according to {@link Object#equals}) after
    129    * the first are ignored.
    130    */
    131   public static <K, V> ImmutableSetMultimap<K, V> of(
    132       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
    133     ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
    134     builder.put(k1, v1);
    135     builder.put(k2, v2);
    136     builder.put(k3, v3);
    137     builder.put(k4, v4);
    138     builder.put(k5, v5);
    139     return builder.build();
    140   }
    141 
    142   // looking for of() with > 5 entries? Use the builder instead.
    143 
    144   /**
    145    * Returns a new {@link Builder}.
    146    */
    147   public static <K, V> Builder<K, V> builder() {
    148     return new Builder<K, V>();
    149   }
    150 
    151   /**
    152    * Multimap for {@link ImmutableSetMultimap.Builder} that maintains key
    153    * and value orderings and performs better than {@link LinkedHashMultimap}.
    154    */
    155   private static class BuilderMultimap<K, V> extends AbstractMapBasedMultimap<K, V> {
    156     BuilderMultimap() {
    157       super(new LinkedHashMap<K, Collection<V>>());
    158     }
    159     @Override Collection<V> createCollection() {
    160       return Sets.newLinkedHashSet();
    161     }
    162     private static final long serialVersionUID = 0;
    163   }
    164 
    165   /**
    166    * A builder for creating immutable {@code SetMultimap} instances, especially
    167    * {@code public static final} multimaps ("constant multimaps"). Example:
    168    * <pre>   {@code
    169    *
    170    *   static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP =
    171    *       new ImmutableSetMultimap.Builder<String, Integer>()
    172    *           .put("one", 1)
    173    *           .putAll("several", 1, 2, 3)
    174    *           .putAll("many", 1, 2, 3, 4, 5)
    175    *           .build();}</pre>
    176    *
    177    * <p>Builder instances can be reused; it is safe to call {@link #build} multiple
    178    * times to build multiple multimaps in series. Each multimap contains the
    179    * key-value mappings in the previously created multimaps.
    180    *
    181    * @since 2.0 (imported from Google Collections Library)
    182    */
    183   public static final class Builder<K, V>
    184       extends ImmutableMultimap.Builder<K, V> {
    185     /**
    186      * Creates a new builder. The returned builder is equivalent to the builder
    187      * generated by {@link ImmutableSetMultimap#builder}.
    188      */
    189     public Builder() {
    190       builderMultimap = new BuilderMultimap<K, V>();
    191     }
    192 
    193     /**
    194      * Adds a key-value mapping to the built multimap if it is not already
    195      * present.
    196      */
    197     @Override public Builder<K, V> put(K key, V value) {
    198       builderMultimap.put(checkNotNull(key), checkNotNull(value));
    199       return this;
    200     }
    201 
    202     /**
    203      * Adds an entry to the built multimap if it is not already present.
    204      *
    205      * @since 11.0
    206      */
    207     @Override public Builder<K, V> put(Entry<? extends K, ? extends V> entry) {
    208       builderMultimap.put(
    209           checkNotNull(entry.getKey()), checkNotNull(entry.getValue()));
    210       return this;
    211     }
    212 
    213     @Override public Builder<K, V> putAll(K key, Iterable<? extends V> values) {
    214       Collection<V> collection = builderMultimap.get(checkNotNull(key));
    215       for (V value : values) {
    216         collection.add(checkNotNull(value));
    217       }
    218       return this;
    219     }
    220 
    221     @Override public Builder<K, V> putAll(K key, V... values) {
    222       return putAll(key, Arrays.asList(values));
    223     }
    224 
    225     @Override public Builder<K, V> putAll(
    226         Multimap<? extends K, ? extends V> multimap) {
    227       for (Entry<? extends K, ? extends Collection<? extends V>> entry
    228           : multimap.asMap().entrySet()) {
    229         putAll(entry.getKey(), entry.getValue());
    230       }
    231       return this;
    232     }
    233 
    234     /**
    235      * {@inheritDoc}
    236      *
    237      * @since 8.0
    238      */
    239     @Override
    240     public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) {
    241       this.keyComparator = checkNotNull(keyComparator);
    242       return this;
    243     }
    244 
    245     /**
    246      * Specifies the ordering of the generated multimap's values for each key.
    247      *
    248      * <p>If this method is called, the sets returned by the {@code get()}
    249      * method of the generated multimap and its {@link Multimap#asMap()} view
    250      * are {@link ImmutableSortedSet} instances. However, serialization does not
    251      * preserve that property, though it does maintain the key and value
    252      * ordering.
    253      *
    254      * @since 8.0
    255      */
    256     // TODO: Make serialization behavior consistent.
    257     @Override
    258     public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {
    259       super.orderValuesBy(valueComparator);
    260       return this;
    261     }
    262 
    263     /**
    264      * Returns a newly-created immutable set multimap.
    265      */
    266     @Override public ImmutableSetMultimap<K, V> build() {
    267       if (keyComparator != null) {
    268         Multimap<K, V> sortedCopy = new BuilderMultimap<K, V>();
    269         List<Map.Entry<K, Collection<V>>> entries = Lists.newArrayList(
    270             builderMultimap.asMap().entrySet());
    271         Collections.sort(
    272             entries,
    273             Ordering.from(keyComparator).<K>onKeys());
    274         for (Map.Entry<K, Collection<V>> entry : entries) {
    275           sortedCopy.putAll(entry.getKey(), entry.getValue());
    276         }
    277         builderMultimap = sortedCopy;
    278       }
    279       return copyOf(builderMultimap, valueComparator);
    280     }
    281   }
    282 
    283   /**
    284    * Returns an immutable set multimap containing the same mappings as
    285    * {@code multimap}. The generated multimap's key and value orderings
    286    * correspond to the iteration ordering of the {@code multimap.asMap()} view.
    287    * Repeated occurrences of an entry in the multimap after the first are
    288    * ignored.
    289    *
    290    * <p>Despite the method name, this method attempts to avoid actually copying
    291    * the data when it is safe to do so. The exact circumstances under which a
    292    * copy will or will not be performed are undocumented and subject to change.
    293    *
    294    * @throws NullPointerException if any key or value in {@code multimap} is
    295    *     null
    296    */
    297   public static <K, V> ImmutableSetMultimap<K, V> copyOf(
    298       Multimap<? extends K, ? extends V> multimap) {
    299     return copyOf(multimap, null);
    300   }
    301 
    302   private static <K, V> ImmutableSetMultimap<K, V> copyOf(
    303       Multimap<? extends K, ? extends V> multimap,
    304       Comparator<? super V> valueComparator) {
    305     checkNotNull(multimap); // eager for GWT
    306     if (multimap.isEmpty() && valueComparator == null) {
    307       return of();
    308     }
    309 
    310     if (multimap instanceof ImmutableSetMultimap) {
    311       @SuppressWarnings("unchecked") // safe since multimap is not writable
    312       ImmutableSetMultimap<K, V> kvMultimap
    313           = (ImmutableSetMultimap<K, V>) multimap;
    314       if (!kvMultimap.isPartialView()) {
    315         return kvMultimap;
    316       }
    317     }
    318 
    319     ImmutableMap.Builder<K, ImmutableSet<V>> builder = ImmutableMap.builder();
    320     int size = 0;
    321 
    322     for (Entry<? extends K, ? extends Collection<? extends V>> entry
    323         : multimap.asMap().entrySet()) {
    324       K key = entry.getKey();
    325       Collection<? extends V> values = entry.getValue();
    326       ImmutableSet<V> set = valueSet(valueComparator, values);
    327       if (!set.isEmpty()) {
    328         builder.put(key, set);
    329         size += set.size();
    330       }
    331     }
    332 
    333     return new ImmutableSetMultimap<K, V>(
    334         builder.build(), size, valueComparator);
    335   }
    336 
    337   /**
    338    * Returned by get() when a missing key is provided. Also holds the
    339    * comparator, if any, used for values.
    340    */
    341   private final transient ImmutableSet<V> emptySet;
    342 
    343   ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size,
    344       @Nullable Comparator<? super V> valueComparator) {
    345     super(map, size);
    346     this.emptySet = emptySet(valueComparator);
    347   }
    348 
    349   // views
    350 
    351   /**
    352    * Returns an immutable set of the values for the given key.  If no mappings
    353    * in the multimap have the provided key, an empty immutable set is returned.
    354    * The values are in the same order as the parameters used to build this
    355    * multimap.
    356    */
    357   @Override public ImmutableSet<V> get(@Nullable K key) {
    358     // This cast is safe as its type is known in constructor.
    359     ImmutableSet<V> set = (ImmutableSet<V>) map.get(key);
    360     return MoreObjects.firstNonNull(set, emptySet);
    361   }
    362 
    363   private transient ImmutableSetMultimap<V, K> inverse;
    364 
    365   /**
    366    * {@inheritDoc}
    367    *
    368    * <p>Because an inverse of a set multimap cannot contain multiple pairs with
    369    * the same key and value, this method returns an {@code ImmutableSetMultimap}
    370    * rather than the {@code ImmutableMultimap} specified in the {@code
    371    * ImmutableMultimap} class.
    372    *
    373    * @since 11.0
    374    */
    375   public ImmutableSetMultimap<V, K> inverse() {
    376     ImmutableSetMultimap<V, K> result = inverse;
    377     return (result == null) ? (inverse = invert()) : result;
    378   }
    379 
    380   private ImmutableSetMultimap<V, K> invert() {
    381     Builder<V, K> builder = builder();
    382     for (Entry<K, V> entry : entries()) {
    383       builder.put(entry.getValue(), entry.getKey());
    384     }
    385     ImmutableSetMultimap<V, K> invertedMultimap = builder.build();
    386     invertedMultimap.inverse = this;
    387     return invertedMultimap;
    388   }
    389 
    390   /**
    391    * Guaranteed to throw an exception and leave the multimap unmodified.
    392    *
    393    * @throws UnsupportedOperationException always
    394    * @deprecated Unsupported operation.
    395    */
    396   @Deprecated @Override public ImmutableSet<V> removeAll(Object key) {
    397     throw new UnsupportedOperationException();
    398   }
    399 
    400   /**
    401    * Guaranteed to throw an exception and leave the multimap unmodified.
    402    *
    403    * @throws UnsupportedOperationException always
    404    * @deprecated Unsupported operation.
    405    */
    406   @Deprecated @Override public ImmutableSet<V> replaceValues(
    407       K key, Iterable<? extends V> values) {
    408     throw new UnsupportedOperationException();
    409   }
    410 
    411   private transient ImmutableSet<Entry<K, V>> entries;
    412 
    413   /**
    414    * Returns an immutable collection of all key-value pairs in the multimap.
    415    * Its iterator traverses the values for the first key, the values for the
    416    * second key, and so on.
    417    */
    418   @Override public ImmutableSet<Entry<K, V>> entries() {
    419     ImmutableSet<Entry<K, V>> result = entries;
    420     return (result == null)
    421         ? (entries = new EntrySet<K, V>(this))
    422         : result;
    423   }
    424 
    425   private static final class EntrySet<K, V> extends ImmutableSet<Entry<K, V>> {
    426     private transient final ImmutableSetMultimap<K, V> multimap;
    427 
    428     EntrySet(ImmutableSetMultimap<K, V> multimap) {
    429       this.multimap = multimap;
    430     }
    431 
    432     @Override
    433     public boolean contains(@Nullable Object object) {
    434       if (object instanceof Entry) {
    435         Entry<?, ?> entry = (Entry<?, ?>) object;
    436         return multimap.containsEntry(entry.getKey(), entry.getValue());
    437       }
    438       return false;
    439     }
    440 
    441     @Override
    442     public int size() {
    443       return multimap.size();
    444     }
    445 
    446     @Override
    447     public UnmodifiableIterator<Entry<K, V>> iterator() {
    448       return multimap.entryIterator();
    449     }
    450 
    451     @Override
    452     boolean isPartialView() {
    453       return false;
    454     }
    455   }
    456 
    457   private static <V> ImmutableSet<V> valueSet(
    458       @Nullable Comparator<? super V> valueComparator,
    459       Collection<? extends V> values) {
    460     return (valueComparator == null)
    461         ? ImmutableSet.copyOf(values)
    462         : ImmutableSortedSet.copyOf(valueComparator, values);
    463   }
    464 
    465   private static <V> ImmutableSet<V> emptySet(
    466       @Nullable Comparator<? super V> valueComparator) {
    467     return (valueComparator == null)
    468         ? ImmutableSet.<V>of()
    469         : ImmutableSortedSet.<V>emptySet(valueComparator);
    470   }
    471 
    472   /**
    473    * @serialData number of distinct keys, and then for each distinct key: the
    474    *     key, the number of values for that key, and the key's values
    475    */
    476   @GwtIncompatible("java.io.ObjectOutputStream")
    477   private void writeObject(ObjectOutputStream stream) throws IOException {
    478     stream.defaultWriteObject();
    479     stream.writeObject(valueComparator());
    480     Serialization.writeMultimap(this, stream);
    481   }
    482 
    483   @Nullable Comparator<? super V> valueComparator() {
    484     return emptySet instanceof ImmutableSortedSet
    485         ? ((ImmutableSortedSet<V>) emptySet).comparator()
    486         : null;
    487   }
    488 
    489   @GwtIncompatible("java.io.ObjectInputStream")
    490   // Serialization type safety is at the caller's mercy.
    491   @SuppressWarnings("unchecked")
    492   private void readObject(ObjectInputStream stream)
    493       throws IOException, ClassNotFoundException {
    494     stream.defaultReadObject();
    495     Comparator<Object> valueComparator =
    496         (Comparator<Object>) stream.readObject();
    497     int keyCount = stream.readInt();
    498     if (keyCount < 0) {
    499       throw new InvalidObjectException("Invalid key count " + keyCount);
    500     }
    501     ImmutableMap.Builder<Object, ImmutableSet<Object>> builder
    502         = ImmutableMap.builder();
    503     int tmpSize = 0;
    504 
    505     for (int i = 0; i < keyCount; i++) {
    506       Object key = stream.readObject();
    507       int valueCount = stream.readInt();
    508       if (valueCount <= 0) {
    509         throw new InvalidObjectException("Invalid value count " + valueCount);
    510       }
    511 
    512       Object[] array = new Object[valueCount];
    513       for (int j = 0; j < valueCount; j++) {
    514         array[j] = stream.readObject();
    515       }
    516       ImmutableSet<Object> valueSet = valueSet(valueComparator, asList(array));
    517       if (valueSet.size() != array.length) {
    518         throw new InvalidObjectException(
    519             "Duplicate key-value pairs exist for key " + key);
    520       }
    521       builder.put(key, valueSet);
    522       tmpSize += valueCount;
    523     }
    524 
    525     ImmutableMap<Object, ImmutableSet<Object>> tmpMap;
    526     try {
    527       tmpMap = builder.build();
    528     } catch (IllegalArgumentException e) {
    529       throw (InvalidObjectException)
    530           new InvalidObjectException(e.getMessage()).initCause(e);
    531     }
    532 
    533     FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap);
    534     FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize);
    535     FieldSettersHolder.EMPTY_SET_FIELD_SETTER.set(
    536         this, emptySet(valueComparator));
    537   }
    538 
    539   @GwtIncompatible("not needed in emulated source.")
    540   private static final long serialVersionUID = 0;
    541 }
    542