View Javadoc
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.Objects.firstNonNull;
20  import static com.google.common.base.Preconditions.checkNotNull;
21  import static java.util.Arrays.asList;
22  
23  import com.google.common.annotations.GwtCompatible;
24  import com.google.common.annotations.GwtIncompatible;
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 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 }