View Javadoc
1   /*
2    * Copyright (C) 2008 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 com.google.common.collect.CollectPreconditions.checkEntryNotNull;
21  
22  import com.google.common.annotations.GwtCompatible;
23  
24  import java.io.Serializable;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.Comparator;
29  import java.util.Iterator;
30  import java.util.LinkedHashMap;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Map.Entry;
34  import java.util.Set;
35  
36  import javax.annotation.Nullable;
37  
38  /**
39   * An immutable {@link Multimap}. Does not permit null keys or values.
40   *
41   * <p>Unlike {@link Multimaps#unmodifiableMultimap(Multimap)}, which is
42   * a <i>view</i> of a separate multimap which can still change, an instance of
43   * {@code ImmutableMultimap} contains its own data and will <i>never</i>
44   * change. {@code ImmutableMultimap} is convenient for
45   * {@code public static final} multimaps ("constant multimaps") and also lets
46   * you easily make a "defensive copy" of a multimap provided to your class by
47   * a caller.
48   *
49   * <p><b>Note:</b> Although this class is not final, it cannot be subclassed as
50   * it has no public or protected constructors. Thus, instances of this class
51   * are guaranteed to be immutable.
52   *
53   * <p>In addition to methods defined by {@link Multimap}, an {@link #inverse}
54   * method is also supported.
55   *
56   * <p>See the Guava User Guide article on <a href=
57   * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
58   * immutable collections</a>.
59   *
60   * @author Jared Levy
61   * @since 2.0 (imported from Google Collections Library)
62   */
63  @GwtCompatible(emulated = true)
64  public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V>
65      implements Serializable {
66  
67    /** Returns an empty multimap. */
68    public static <K, V> ImmutableMultimap<K, V> of() {
69      return ImmutableListMultimap.of();
70    }
71  
72    /**
73     * Returns an immutable multimap containing a single entry.
74     */
75    public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1) {
76      return ImmutableListMultimap.of(k1, v1);
77    }
78  
79    /**
80     * Returns an immutable multimap containing the given entries, in order.
81     */
82    public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1, K k2, V v2) {
83      return ImmutableListMultimap.of(k1, v1, k2, v2);
84    }
85  
86    /**
87     * Returns an immutable multimap containing the given entries, in order.
88     */
89    public static <K, V> ImmutableMultimap<K, V> of(
90        K k1, V v1, K k2, V v2, K k3, V v3) {
91      return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3);
92    }
93  
94    /**
95     * Returns an immutable multimap containing the given entries, in order.
96     */
97    public static <K, V> ImmutableMultimap<K, V> of(
98        K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
99      return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4);
100   }
101 
102   /**
103    * Returns an immutable multimap containing the given entries, in order.
104    */
105   public static <K, V> ImmutableMultimap<K, V> of(
106       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
107     return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
108   }
109 
110   // looking for of() with > 5 entries? Use the builder instead.
111 
112   /**
113    * Returns a new builder. The generated builder is equivalent to the builder
114    * created by the {@link Builder} constructor.
115    */
116   public static <K, V> Builder<K, V> builder() {
117     return new Builder<K, V>();
118   }
119 
120   /**
121    * Multimap for {@link ImmutableMultimap.Builder} that maintains key and
122    * value orderings, allows duplicate values, and performs better than
123    * {@link LinkedListMultimap}.
124    */
125   private static class BuilderMultimap<K, V> extends AbstractMapBasedMultimap<K, V> {
126     BuilderMultimap() {
127       super(new LinkedHashMap<K, Collection<V>>());
128     }
129     @Override Collection<V> createCollection() {
130       return Lists.newArrayList();
131     }
132     private static final long serialVersionUID = 0;
133   }
134 
135   /**
136    * A builder for creating immutable multimap instances, especially
137    * {@code public static final} multimaps ("constant multimaps"). Example:
138    * <pre>   {@code
139    *
140    *   static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP =
141    *       new ImmutableMultimap.Builder<String, Integer>()
142    *           .put("one", 1)
143    *           .putAll("several", 1, 2, 3)
144    *           .putAll("many", 1, 2, 3, 4, 5)
145    *           .build();}</pre>
146    *
147    * <p>Builder instances can be reused; it is safe to call {@link #build} multiple
148    * times to build multiple multimaps in series. Each multimap contains the
149    * key-value mappings in the previously created multimaps.
150    *
151    * @since 2.0 (imported from Google Collections Library)
152    */
153   public static class Builder<K, V> {
154     Multimap<K, V> builderMultimap = new BuilderMultimap<K, V>();
155     Comparator<? super K> keyComparator;
156     Comparator<? super V> valueComparator;
157 
158     /**
159      * Creates a new builder. The returned builder is equivalent to the builder
160      * generated by {@link ImmutableMultimap#builder}.
161      */
162     public Builder() {}
163 
164     /**
165      * Adds a key-value mapping to the built multimap.
166      */
167     public Builder<K, V> put(K key, V value) {
168       checkEntryNotNull(key, value);
169       builderMultimap.put(key, value);
170       return this;
171     }
172 
173     /**
174      * Adds an entry to the built multimap.
175      *
176      * @since 11.0
177      */
178     public Builder<K, V> put(Entry<? extends K, ? extends V> entry) {
179       return put(entry.getKey(), entry.getValue());
180     }
181 
182     /**
183      * Stores a collection of values with the same key in the built multimap.
184      *
185      * @throws NullPointerException if {@code key}, {@code values}, or any
186      *     element in {@code values} is null. The builder is left in an invalid
187      *     state.
188      */
189     public Builder<K, V> putAll(K key, Iterable<? extends V> values) {
190       if (key == null) {
191         throw new NullPointerException(
192             "null key in entry: null=" + Iterables.toString(values));
193       }
194       Collection<V> valueList = builderMultimap.get(key);
195       for (V value : values) {
196         checkEntryNotNull(key, value);
197         valueList.add(value);
198       }
199       return this;
200     }
201 
202     /**
203      * Stores an array of values with the same key in the built multimap.
204      *
205      * @throws NullPointerException if the key or any value is null. The builder
206      *     is left in an invalid state.
207      */
208     public Builder<K, V> putAll(K key, V... values) {
209       return putAll(key, Arrays.asList(values));
210     }
211 
212     /**
213      * Stores another multimap's entries in the built multimap. The generated
214      * multimap's key and value orderings correspond to the iteration ordering
215      * of the {@code multimap.asMap()} view, with new keys and values following
216      * any existing keys and values.
217      *
218      * @throws NullPointerException if any key or value in {@code multimap} is
219      *     null. The builder is left in an invalid state.
220      */
221     public Builder<K, V> putAll(Multimap<? extends K, ? extends V> multimap) {
222       for (Entry<? extends K, ? extends Collection<? extends V>> entry
223           : multimap.asMap().entrySet()) {
224         putAll(entry.getKey(), entry.getValue());
225       }
226       return this;
227     }
228 
229     /**
230      * Specifies the ordering of the generated multimap's keys.
231      *
232      * @since 8.0
233      */
234     public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) {
235       this.keyComparator = checkNotNull(keyComparator);
236       return this;
237     }
238 
239     /**
240      * Specifies the ordering of the generated multimap's values for each key.
241      *
242      * @since 8.0
243      */
244     public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {
245       this.valueComparator = checkNotNull(valueComparator);
246       return this;
247     }
248 
249     /**
250      * Returns a newly-created immutable multimap.
251      */
252     public ImmutableMultimap<K, V> build() {
253       if (valueComparator != null) {
254         for (Collection<V> values : builderMultimap.asMap().values()) {
255           List<V> list = (List <V>) values;
256           Collections.sort(list, valueComparator);
257         }
258       }
259       if (keyComparator != null) {
260         Multimap<K, V> sortedCopy = new BuilderMultimap<K, V>();
261         List<Map.Entry<K, Collection<V>>> entries = Lists.newArrayList(
262             builderMultimap.asMap().entrySet());
263         Collections.sort(
264             entries,
265             Ordering.from(keyComparator).<K>onKeys());
266         for (Map.Entry<K, Collection<V>> entry : entries) {
267           sortedCopy.putAll(entry.getKey(), entry.getValue());
268         }
269         builderMultimap = sortedCopy;
270       }
271       return copyOf(builderMultimap);
272     }
273   }
274 
275   /**
276    * Returns an immutable multimap containing the same mappings as {@code
277    * multimap}. The generated multimap's key and value orderings correspond to
278    * the iteration ordering of the {@code multimap.asMap()} view.
279    *
280    * <p>Despite the method name, this method attempts to avoid actually copying
281    * the data when it is safe to do so. The exact circumstances under which a
282    * copy will or will not be performed are undocumented and subject to change.
283    *
284    * @throws NullPointerException if any key or value in {@code multimap} is
285    *         null
286    */
287   public static <K, V> ImmutableMultimap<K, V> copyOf(
288       Multimap<? extends K, ? extends V> multimap) {
289     if (multimap instanceof ImmutableMultimap) {
290       @SuppressWarnings("unchecked") // safe since multimap is not writable
291       ImmutableMultimap<K, V> kvMultimap
292           = (ImmutableMultimap<K, V>) multimap;
293       if (!kvMultimap.isPartialView()) {
294         return kvMultimap;
295       }
296     }
297     return ImmutableListMultimap.copyOf(multimap);
298   }
299 
300   final transient ImmutableMap<K, ? extends ImmutableCollection<V>> map;
301   final transient int size;
302 
303   // These constants allow the deserialization code to set final fields. This
304   // holder class makes sure they are not initialized unless an instance is
305   // deserialized.
306 
307   ImmutableMultimap(ImmutableMap<K, ? extends ImmutableCollection<V>> map,
308       int size) {
309     this.map = map;
310     this.size = size;
311   }
312 
313   // mutators (not supported)
314 
315   /**
316    * Guaranteed to throw an exception and leave the multimap unmodified.
317    *
318    * @throws UnsupportedOperationException always
319    * @deprecated Unsupported operation.
320    */
321   @Deprecated
322   @Override
323   public ImmutableCollection<V> removeAll(Object key) {
324     throw new UnsupportedOperationException();
325   }
326 
327   /**
328    * Guaranteed to throw an exception and leave the multimap unmodified.
329    *
330    * @throws UnsupportedOperationException always
331    * @deprecated Unsupported operation.
332    */
333   @Deprecated
334   @Override
335   public ImmutableCollection<V> replaceValues(K key,
336       Iterable<? extends V> values) {
337     throw new UnsupportedOperationException();
338   }
339 
340   /**
341    * Guaranteed to throw an exception and leave the multimap unmodified.
342    *
343    * @throws UnsupportedOperationException always
344    * @deprecated Unsupported operation.
345    */
346   @Deprecated
347   @Override
348   public void clear() {
349     throw new UnsupportedOperationException();
350   }
351 
352   /**
353    * Returns an immutable collection of the values for the given key.  If no
354    * mappings in the multimap have the provided key, an empty immutable
355    * collection is returned. The values are in the same order as the parameters
356    * used to build this multimap.
357    */
358   @Override
359   public abstract ImmutableCollection<V> get(K key);
360 
361   /**
362    * Returns an immutable multimap which is the inverse of this one. For every
363    * key-value mapping in the original, the result will have a mapping with
364    * key and value reversed.
365    *
366    * @since 11.0
367    */
368   public abstract ImmutableMultimap<V, K> inverse();
369 
370   /**
371    * Guaranteed to throw an exception and leave the multimap unmodified.
372    *
373    * @throws UnsupportedOperationException always
374    * @deprecated Unsupported operation.
375    */
376   @Deprecated
377   @Override
378   public boolean put(K key, V value) {
379     throw new UnsupportedOperationException();
380   }
381 
382   /**
383    * Guaranteed to throw an exception and leave the multimap unmodified.
384    *
385    * @throws UnsupportedOperationException always
386    * @deprecated Unsupported operation.
387    */
388   @Deprecated
389   @Override
390   public boolean putAll(K key, Iterable<? extends V> values) {
391     throw new UnsupportedOperationException();
392   }
393 
394   /**
395    * Guaranteed to throw an exception and leave the multimap unmodified.
396    *
397    * @throws UnsupportedOperationException always
398    * @deprecated Unsupported operation.
399    */
400   @Deprecated
401   @Override
402   public boolean putAll(Multimap<? extends K, ? extends V> multimap) {
403     throw new UnsupportedOperationException();
404   }
405 
406   /**
407    * Guaranteed to throw an exception and leave the multimap unmodified.
408    *
409    * @throws UnsupportedOperationException always
410    * @deprecated Unsupported operation.
411    */
412   @Deprecated
413   @Override
414   public boolean remove(Object key, Object value) {
415     throw new UnsupportedOperationException();
416   }
417   
418   /**
419    * Returns {@code true} if this immutable multimap's implementation contains references to
420    * user-created objects that aren't accessible via this multimap's methods. This is generally
421    * used to determine whether {@code copyOf} implementations should make an explicit copy to avoid
422    * memory leaks.
423    */
424   boolean isPartialView() {
425     return map.isPartialView();
426   }
427 
428   // accessors
429 
430   @Override
431   public boolean containsKey(@Nullable Object key) {
432     return map.containsKey(key);
433   }
434 
435   @Override
436   public boolean containsValue(@Nullable Object value) {
437     return value != null && super.containsValue(value);
438   }
439   
440   @Override
441   public int size() {
442     return size;
443   }
444 
445   // views
446 
447   /**
448    * Returns an immutable set of the distinct keys in this multimap. These keys
449    * are ordered according to when they first appeared during the construction
450    * of this multimap.
451    */
452   @Override
453   public ImmutableSet<K> keySet() {
454     return map.keySet();
455   }
456 
457   /**
458    * Returns an immutable map that associates each key with its corresponding
459    * values in the multimap.
460    */
461   @Override
462   @SuppressWarnings("unchecked") // a widening cast
463   public ImmutableMap<K, Collection<V>> asMap() {
464     return (ImmutableMap) map;
465   }
466   
467   @Override
468   Map<K, Collection<V>> createAsMap() {
469     throw new AssertionError("should never be called");
470   }
471 
472   /**
473    * Returns an immutable collection of all key-value pairs in the multimap. Its
474    * iterator traverses the values for the first key, the values for the second
475    * key, and so on.
476    */
477   @Override
478   public ImmutableCollection<Entry<K, V>> entries() {
479     return (ImmutableCollection<Entry<K, V>>) super.entries();
480   }
481   
482   @Override
483   ImmutableCollection<Entry<K, V>> createEntries() {
484     return new EntryCollection<K, V>(this);
485   }
486 
487   private static class EntryCollection<K, V>
488       extends ImmutableCollection<Entry<K, V>> {
489     final ImmutableMultimap<K, V> multimap;
490 
491     EntryCollection(ImmutableMultimap<K, V> multimap) {
492       this.multimap = multimap;
493     }
494 
495     @Override public UnmodifiableIterator<Entry<K, V>> iterator() {
496       return multimap.entryIterator();
497     }
498 
499     @Override boolean isPartialView() {
500       return multimap.isPartialView();
501     }
502 
503     @Override
504     public int size() {
505       return multimap.size();
506     }
507 
508     @Override public boolean contains(Object object) {
509       if (object instanceof Entry) {
510         Entry<?, ?> entry = (Entry<?, ?>) object;
511         return multimap.containsEntry(entry.getKey(), entry.getValue());
512       }
513       return false;
514     }
515 
516     private static final long serialVersionUID = 0;
517   }
518   
519   private abstract class Itr<T> extends UnmodifiableIterator<T> {
520     final Iterator<Entry<K, Collection<V>>> mapIterator = asMap().entrySet().iterator();
521     K key = null;
522     Iterator<V> valueIterator = Iterators.emptyIterator();
523     
524     abstract T output(K key, V value);
525 
526     @Override
527     public boolean hasNext() {
528       return mapIterator.hasNext() || valueIterator.hasNext();
529     }
530 
531     @Override
532     public T next() {
533       if (!valueIterator.hasNext()) {
534         Entry<K, Collection<V>> mapEntry = mapIterator.next();
535         key = mapEntry.getKey();
536         valueIterator = mapEntry.getValue().iterator();
537       }
538       return output(key, valueIterator.next());
539     }
540   }
541   
542   @Override
543   UnmodifiableIterator<Entry<K, V>> entryIterator() {
544     return new Itr<Entry<K, V>>() {
545       @Override
546       Entry<K, V> output(K key, V value) {
547         return Maps.immutableEntry(key, value);
548       }
549     };
550   }
551 
552   /**
553    * Returns a collection, which may contain duplicates, of all keys. The number
554    * of times a key appears in the returned multiset equals the number of
555    * mappings the key has in the multimap. Duplicate keys appear consecutively
556    * in the multiset's iteration order.
557    */
558   @Override
559   public ImmutableMultiset<K> keys() {
560     return (ImmutableMultiset<K>) super.keys();
561   }
562 
563   @Override
564   ImmutableMultiset<K> createKeys() {
565     return new Keys();
566   }
567 
568   @SuppressWarnings("serial") // Uses writeReplace, not default serialization
569   class Keys extends ImmutableMultiset<K> {
570     @Override
571     public boolean contains(@Nullable Object object) {
572       return containsKey(object);
573     }
574 
575     @Override
576     public int count(@Nullable Object element) {
577       Collection<V> values = map.get(element);
578       return (values == null) ? 0 : values.size();
579     }
580 
581     @Override
582     public Set<K> elementSet() {
583       return keySet();
584     }
585 
586     @Override
587     public int size() {
588       return ImmutableMultimap.this.size();
589     }
590     
591     @Override
592     Multiset.Entry<K> getEntry(int index) {
593       Map.Entry<K, ? extends Collection<V>> entry = map.entrySet().asList().get(index);
594       return Multisets.immutableEntry(entry.getKey(), entry.getValue().size());
595     }
596 
597     @Override
598     boolean isPartialView() {
599       return true;
600     }
601   }
602 
603   /**
604    * Returns an immutable collection of the values in this multimap. Its
605    * iterator traverses the values for the first key, the values for the second
606    * key, and so on.
607    */
608   @Override
609   public ImmutableCollection<V> values() {
610     return (ImmutableCollection<V>) super.values();
611   }
612   
613   @Override
614   ImmutableCollection<V> createValues() {
615     return new Values<K, V>(this);
616   }
617 
618   @Override
619   UnmodifiableIterator<V> valueIterator() {
620     return new Itr<V>() {
621       @Override
622       V output(K key, V value) {
623         return value;
624       }
625     };
626   }
627 
628   private static final class Values<K, V> extends ImmutableCollection<V> {
629     private transient final ImmutableMultimap<K, V> multimap;
630     
631     Values(ImmutableMultimap<K, V> multimap) {
632       this.multimap = multimap;
633     }
634 
635     @Override
636     public boolean contains(@Nullable Object object) {
637       return multimap.containsValue(object);
638     }
639     
640     @Override public UnmodifiableIterator<V> iterator() {
641       return multimap.valueIterator();
642     }
643 
644     @Override
645     public int size() {
646       return multimap.size();
647     }
648 
649     @Override boolean isPartialView() {
650       return true;
651     }
652 
653     private static final long serialVersionUID = 0;
654   }
655 
656   private static final long serialVersionUID = 0;
657 }
658