• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Beta;
23 import com.google.common.annotations.GwtCompatible;
24 import com.google.common.collect.ImmutableMapEntry.TerminalEntry;
25 
26 import java.io.Serializable;
27 import java.util.Collections;
28 import java.util.EnumMap;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.Map;
32 
33 import javax.annotation.Nullable;
34 
35 /**
36  * An immutable, hash-based {@link Map} with reliable user-specified iteration
37  * order. Does not permit null keys or values.
38  *
39  * <p>Unlike {@link Collections#unmodifiableMap}, which is a <i>view</i> of a
40  * separate map which can still change, an instance of {@code ImmutableMap}
41  * contains its own data and will <i>never</i> change. {@code ImmutableMap} is
42  * convenient for {@code public static final} maps ("constant maps") and also
43  * lets you easily make a "defensive copy" of a map provided to your class by a
44  * caller.
45  *
46  * <p><i>Performance notes:</i> unlike {@link HashMap}, {@code ImmutableMap} is
47  * not optimized for element types that have slow {@link Object#equals} or
48  * {@link Object#hashCode} implementations. You can get better performance by
49  * having your element type cache its own hash codes, and by making use of the
50  * cached values to short-circuit a slow {@code equals} algorithm.
51  *
52  * <p>See the Guava User Guide article on <a href=
53  * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
54  * immutable collections</a>.
55  *
56  * @author Jesse Wilson
57  * @author Kevin Bourrillion
58  * @since 2.0 (imported from Google Collections Library)
59  */
60 @GwtCompatible(serializable = true, emulated = true)
61 @SuppressWarnings("serial") // we're overriding default serialization
62 public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
63 
64   /**
65    * Returns the empty map. This map behaves and performs comparably to
66    * {@link Collections#emptyMap}, and is preferable mainly for consistency
67    * and maintainability of your code.
68    */
of()69   public static <K, V> ImmutableMap<K, V> of() {
70     return ImmutableBiMap.of();
71   }
72 
73   /**
74    * Returns an immutable map containing a single entry. This map behaves and
75    * performs comparably to {@link Collections#singletonMap} but will not accept
76    * a null key or value. It is preferable mainly for consistency and
77    * maintainability of your code.
78    */
of(K k1, V v1)79   public static <K, V> ImmutableMap<K, V> of(K k1, V v1) {
80     return ImmutableBiMap.of(k1, v1);
81   }
82 
83   /**
84    * Returns an immutable map containing the given entries, in order.
85    *
86    * @throws IllegalArgumentException if duplicate keys are provided
87    */
of(K k1, V v1, K k2, V v2)88   public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2) {
89     return new RegularImmutableMap<K, V>(entryOf(k1, v1), entryOf(k2, v2));
90   }
91 
92   /**
93    * Returns an immutable map containing the given entries, in order.
94    *
95    * @throws IllegalArgumentException if duplicate keys are provided
96    */
of( K k1, V v1, K k2, V v2, K k3, V v3)97   public static <K, V> ImmutableMap<K, V> of(
98       K k1, V v1, K k2, V v2, K k3, V v3) {
99     return new RegularImmutableMap<K, V>(
100         entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3));
101   }
102 
103   /**
104    * Returns an immutable map containing the given entries, in order.
105    *
106    * @throws IllegalArgumentException if duplicate keys are provided
107    */
of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4)108   public static <K, V> ImmutableMap<K, V> of(
109       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
110     return new RegularImmutableMap<K, V>(
111         entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4));
112   }
113 
114   /**
115    * Returns an immutable map containing the given entries, in order.
116    *
117    * @throws IllegalArgumentException if duplicate keys are provided
118    */
of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5)119   public static <K, V> ImmutableMap<K, V> of(
120       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
121     return new RegularImmutableMap<K, V>(entryOf(k1, v1),
122         entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5));
123   }
124 
125   // looking for of() with > 5 entries? Use the builder instead.
126 
127   /**
128    * Verifies that {@code key} and {@code value} are non-null, and returns a new
129    * immutable entry with those values.
130    *
131    * <p>A call to {@link Map.Entry#setValue} on the returned entry will always
132    * throw {@link UnsupportedOperationException}.
133    */
entryOf(K key, V value)134   static <K, V> TerminalEntry<K, V> entryOf(K key, V value) {
135     checkEntryNotNull(key, value);
136     return new TerminalEntry<K, V>(key, value);
137   }
138 
139   /**
140    * Returns a new builder. The generated builder is equivalent to the builder
141    * created by the {@link Builder} constructor.
142    */
builder()143   public static <K, V> Builder<K, V> builder() {
144     return new Builder<K, V>();
145   }
146 
checkNoConflict(boolean safe, String conflictDescription, Entry<?, ?> entry1, Entry<?, ?> entry2)147   static void checkNoConflict(boolean safe, String conflictDescription,
148       Entry<?, ?> entry1, Entry<?, ?> entry2) {
149     if (!safe) {
150       throw new IllegalArgumentException(
151           "Multiple entries with same " + conflictDescription + ": " + entry1 + " and " + entry2);
152     }
153   }
154 
155   /**
156    * A builder for creating immutable map instances, especially {@code public
157    * static final} maps ("constant maps"). Example: <pre>   {@code
158    *
159    *   static final ImmutableMap<String, Integer> WORD_TO_INT =
160    *       new ImmutableMap.Builder<String, Integer>()
161    *           .put("one", 1)
162    *           .put("two", 2)
163    *           .put("three", 3)
164    *           .build();}</pre>
165    *
166    * <p>For <i>small</i> immutable maps, the {@code ImmutableMap.of()} methods are
167    * even more convenient.
168    *
169    * <p>Builder instances can be reused - it is safe to call {@link #build}
170    * multiple times to build multiple maps in series. Each map is a superset of
171    * the maps created before it.
172    *
173    * @since 2.0 (imported from Google Collections Library)
174    */
175   public static class Builder<K, V> {
176     TerminalEntry<K, V>[] entries;
177     int size;
178 
179     /**
180      * Creates a new builder. The returned builder is equivalent to the builder
181      * generated by {@link ImmutableMap#builder}.
182      */
Builder()183     public Builder() {
184       this(ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY);
185     }
186 
187     @SuppressWarnings("unchecked")
Builder(int initialCapacity)188     Builder(int initialCapacity) {
189       this.entries = new TerminalEntry[initialCapacity];
190       this.size = 0;
191     }
192 
ensureCapacity(int minCapacity)193     private void ensureCapacity(int minCapacity) {
194       if (minCapacity > entries.length) {
195         entries = ObjectArrays.arraysCopyOf(
196             entries, ImmutableCollection.Builder.expandedCapacity(entries.length, minCapacity));
197       }
198     }
199 
200     /**
201      * Associates {@code key} with {@code value} in the built map. Duplicate
202      * keys are not allowed, and will cause {@link #build} to fail.
203      */
put(K key, V value)204     public Builder<K, V> put(K key, V value) {
205       ensureCapacity(size + 1);
206       TerminalEntry<K, V> entry = entryOf(key, value);
207       // don't inline this: we want to fail atomically if key or value is null
208       entries[size++] = entry;
209       return this;
210     }
211 
212     /**
213      * Adds the given {@code entry} to the map, making it immutable if
214      * necessary. Duplicate keys are not allowed, and will cause {@link #build}
215      * to fail.
216      *
217      * @since 11.0
218      */
put(Entry<? extends K, ? extends V> entry)219     public Builder<K, V> put(Entry<? extends K, ? extends V> entry) {
220       return put(entry.getKey(), entry.getValue());
221     }
222 
223     /**
224      * Associates all of the given map's keys and values in the built map.
225      * Duplicate keys are not allowed, and will cause {@link #build} to fail.
226      *
227      * @throws NullPointerException if any key or value in {@code map} is null
228      */
putAll(Map<? extends K, ? extends V> map)229     public Builder<K, V> putAll(Map<? extends K, ? extends V> map) {
230       ensureCapacity(size + map.size());
231       for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
232         put(entry);
233       }
234       return this;
235     }
236 
237     /*
238      * TODO(kevinb): Should build() and the ImmutableBiMap & ImmutableSortedMap
239      * versions throw an IllegalStateException instead?
240      */
241 
242     /**
243      * Returns a newly-created immutable map.
244      *
245      * @throws IllegalArgumentException if duplicate keys were added
246      */
build()247     public ImmutableMap<K, V> build() {
248       switch (size) {
249         case 0:
250           return of();
251         case 1:
252           return of(entries[0].getKey(), entries[0].getValue());
253         default:
254           return new RegularImmutableMap<K, V>(size, entries);
255       }
256     }
257   }
258 
259   /**
260    * Returns an immutable map containing the same entries as {@code map}. If
261    * {@code map} somehow contains entries with duplicate keys (for example, if
262    * it is a {@code SortedMap} whose comparator is not <i>consistent with
263    * equals</i>), the results of this method are undefined.
264    *
265    * <p>Despite the method name, this method attempts to avoid actually copying
266    * the data when it is safe to do so. The exact circumstances under which a
267    * copy will or will not be performed are undocumented and subject to change.
268    *
269    * @throws NullPointerException if any key or value in {@code map} is null
270    */
copyOf( Map<? extends K, ? extends V> map)271   public static <K, V> ImmutableMap<K, V> copyOf(
272       Map<? extends K, ? extends V> map) {
273     if ((map instanceof ImmutableMap) && !(map instanceof ImmutableSortedMap)) {
274       // TODO(user): Make ImmutableMap.copyOf(immutableBiMap) call copyOf()
275       // on the ImmutableMap delegate(), rather than the bimap itself
276 
277       @SuppressWarnings("unchecked") // safe since map is not writable
278       ImmutableMap<K, V> kvMap = (ImmutableMap<K, V>) map;
279       if (!kvMap.isPartialView()) {
280         return kvMap;
281       }
282     } else if (map instanceof EnumMap) {
283       return copyOfEnumMapUnsafe(map);
284     }
285     Entry<?, ?>[] entries = map.entrySet().toArray(EMPTY_ENTRY_ARRAY);
286     switch (entries.length) {
287       case 0:
288         return of();
289       case 1:
290         @SuppressWarnings("unchecked") // all entries will be Entry<K, V>'s
291         Entry<K, V> onlyEntry = (Entry<K, V>) entries[0];
292         return of(onlyEntry.getKey(), onlyEntry.getValue());
293       default:
294         return new RegularImmutableMap<K, V>(entries);
295     }
296   }
297 
298   // If the map is an EnumMap, it must have key type K for some <K extends Enum<K>>.
299   @SuppressWarnings({"unchecked", "rawtypes"})
copyOfEnumMapUnsafe(Map<? extends K, ? extends V> map)300   private static <K, V> ImmutableMap<K, V> copyOfEnumMapUnsafe(Map<? extends K, ? extends V> map) {
301     return copyOfEnumMap((EnumMap) map);
302   }
303 
copyOfEnumMap( Map<K, ? extends V> original)304   private static <K extends Enum<K>, V> ImmutableMap<K, V> copyOfEnumMap(
305       Map<K, ? extends V> original) {
306     EnumMap<K, V> copy = new EnumMap<K, V>(original);
307     for (Map.Entry<?, ?> entry : copy.entrySet()) {
308       checkEntryNotNull(entry.getKey(), entry.getValue());
309     }
310     return ImmutableEnumMap.asImmutable(copy);
311   }
312 
313   private static final Entry<?, ?>[] EMPTY_ENTRY_ARRAY = new Entry<?, ?>[0];
314 
ImmutableMap()315   ImmutableMap() {}
316 
317   /**
318    * Guaranteed to throw an exception and leave the map unmodified.
319    *
320    * @throws UnsupportedOperationException always
321    * @deprecated Unsupported operation.
322    */
323   @Deprecated
324   @Override
put(K k, V v)325   public final V put(K k, V v) {
326     throw new UnsupportedOperationException();
327   }
328 
329   /**
330    * Guaranteed to throw an exception and leave the map unmodified.
331    *
332    * @throws UnsupportedOperationException always
333    * @deprecated Unsupported operation.
334    */
335   @Deprecated
336   @Override
remove(Object o)337   public final V remove(Object o) {
338     throw new UnsupportedOperationException();
339   }
340 
341   /**
342    * Guaranteed to throw an exception and leave the map unmodified.
343    *
344    * @throws UnsupportedOperationException always
345    * @deprecated Unsupported operation.
346    */
347   @Deprecated
348   @Override
putAll(Map<? extends K, ? extends V> map)349   public final void putAll(Map<? extends K, ? extends V> map) {
350     throw new UnsupportedOperationException();
351   }
352 
353   /**
354    * Guaranteed to throw an exception and leave the map unmodified.
355    *
356    * @throws UnsupportedOperationException always
357    * @deprecated Unsupported operation.
358    */
359   @Deprecated
360   @Override
clear()361   public final void clear() {
362     throw new UnsupportedOperationException();
363   }
364 
365   @Override
isEmpty()366   public boolean isEmpty() {
367     return size() == 0;
368   }
369 
370   @Override
containsKey(@ullable Object key)371   public boolean containsKey(@Nullable Object key) {
372     return get(key) != null;
373   }
374 
375   @Override
containsValue(@ullable Object value)376   public boolean containsValue(@Nullable Object value) {
377     return values().contains(value);
378   }
379 
380   // Overriding to mark it Nullable
381   @Override
get(@ullable Object key)382   public abstract V get(@Nullable Object key);
383 
384   private transient ImmutableSet<Entry<K, V>> entrySet;
385 
386   /**
387    * Returns an immutable set of the mappings in this map. The entries are in
388    * the same order as the parameters used to build this map.
389    */
390   @Override
entrySet()391   public ImmutableSet<Entry<K, V>> entrySet() {
392     ImmutableSet<Entry<K, V>> result = entrySet;
393     return (result == null) ? entrySet = createEntrySet() : result;
394   }
395 
createEntrySet()396   abstract ImmutableSet<Entry<K, V>> createEntrySet();
397 
398   private transient ImmutableSet<K> keySet;
399 
400   /**
401    * Returns an immutable set of the keys in this map. These keys are in
402    * the same order as the parameters used to build this map.
403    */
404   @Override
keySet()405   public ImmutableSet<K> keySet() {
406     ImmutableSet<K> result = keySet;
407     return (result == null) ? keySet = createKeySet() : result;
408   }
409 
createKeySet()410   ImmutableSet<K> createKeySet() {
411     return new ImmutableMapKeySet<K, V>(this);
412   }
413 
414   private transient ImmutableCollection<V> values;
415 
416   /**
417    * Returns an immutable collection of the values in this map. The values are
418    * in the same order as the parameters used to build this map.
419    */
420   @Override
values()421   public ImmutableCollection<V> values() {
422     ImmutableCollection<V> result = values;
423     return (result == null) ? values = new ImmutableMapValues<K, V>(this) : result;
424   }
425 
426   // cached so that this.multimapView().inverse() only computes inverse once
427   private transient ImmutableSetMultimap<K, V> multimapView;
428 
429   /**
430    * Returns a multimap view of the map.
431    *
432    * @since 14.0
433    */
434   @Beta
asMultimap()435   public ImmutableSetMultimap<K, V> asMultimap() {
436     ImmutableSetMultimap<K, V> result = multimapView;
437     return (result == null) ? (multimapView = createMultimapView()) : result;
438   }
439 
createMultimapView()440   private ImmutableSetMultimap<K, V> createMultimapView() {
441     ImmutableMap<K, ImmutableSet<V>> map = viewMapValuesAsSingletonSets();
442     return new ImmutableSetMultimap<K, V>(map, map.size(), null);
443   }
444 
viewMapValuesAsSingletonSets()445   private ImmutableMap<K, ImmutableSet<V>> viewMapValuesAsSingletonSets() {
446     return new MapViewOfValuesAsSingletonSets<K, V>(this);
447   }
448 
449   private static final class MapViewOfValuesAsSingletonSets<K, V>
450       extends ImmutableMap<K, ImmutableSet<V>> {
451     private final ImmutableMap<K, V> delegate;
452 
MapViewOfValuesAsSingletonSets(ImmutableMap<K, V> delegate)453     MapViewOfValuesAsSingletonSets(ImmutableMap<K, V> delegate) {
454       this.delegate = checkNotNull(delegate);
455     }
456 
size()457     @Override public int size() {
458       return delegate.size();
459     }
460 
containsKey(@ullable Object key)461     @Override public boolean containsKey(@Nullable Object key) {
462       return delegate.containsKey(key);
463     }
464 
get(@ullable Object key)465     @Override public ImmutableSet<V> get(@Nullable Object key) {
466       V outerValue = delegate.get(key);
467       return (outerValue == null) ? null : ImmutableSet.of(outerValue);
468     }
469 
isPartialView()470     @Override boolean isPartialView() {
471       return false;
472     }
473 
createEntrySet()474     @Override ImmutableSet<Entry<K, ImmutableSet<V>>> createEntrySet() {
475       return new ImmutableMapEntrySet<K, ImmutableSet<V>>() {
476         @Override ImmutableMap<K, ImmutableSet<V>> map() {
477           return MapViewOfValuesAsSingletonSets.this;
478         }
479 
480         @Override
481         public UnmodifiableIterator<Entry<K, ImmutableSet<V>>> iterator() {
482           final Iterator<Entry<K, V>> backingIterator = delegate.entrySet().iterator();
483           return new UnmodifiableIterator<Entry<K, ImmutableSet<V>>>() {
484             @Override public boolean hasNext() {
485               return backingIterator.hasNext();
486             }
487 
488             @Override public Entry<K, ImmutableSet<V>> next() {
489               final Entry<K, V> backingEntry = backingIterator.next();
490               return new AbstractMapEntry<K, ImmutableSet<V>>() {
491                 @Override public K getKey() {
492                   return backingEntry.getKey();
493                 }
494 
495                 @Override public ImmutableSet<V> getValue() {
496                   return ImmutableSet.of(backingEntry.getValue());
497                 }
498               };
499             }
500           };
501         }
502       };
503     }
504   }
505 
506   @Override public boolean equals(@Nullable Object object) {
507     return Maps.equalsImpl(this, object);
508   }
509 
510   abstract boolean isPartialView();
511 
512   @Override public int hashCode() {
513     // not caching hash code since it could change if map values are mutable
514     // in a way that modifies their hash codes
515     return entrySet().hashCode();
516   }
517 
518   @Override public String toString() {
519     return Maps.toStringImpl(this);
520   }
521 
522   /**
523    * Serialized type for all ImmutableMap instances. It captures the logical
524    * contents and they are reconstructed using public factory methods. This
525    * ensures that the implementation types remain as implementation details.
526    */
527   static class SerializedForm implements Serializable {
528     private final Object[] keys;
529     private final Object[] values;
530     SerializedForm(ImmutableMap<?, ?> map) {
531       keys = new Object[map.size()];
532       values = new Object[map.size()];
533       int i = 0;
534       for (Entry<?, ?> entry : map.entrySet()) {
535         keys[i] = entry.getKey();
536         values[i] = entry.getValue();
537         i++;
538       }
539     }
540     Object readResolve() {
541       Builder<Object, Object> builder = new Builder<Object, Object>();
542       return createMap(builder);
543     }
544     Object createMap(Builder<Object, Object> builder) {
545       for (int i = 0; i < keys.length; i++) {
546         builder.put(keys[i], values[i]);
547       }
548       return builder.build();
549     }
550     private static final long serialVersionUID = 0;
551   }
552 
553   Object writeReplace() {
554     return new SerializedForm(this);
555   }
556 }
557