• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Google Inc.
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 com.google.common.annotations.GwtCompatible;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.common.collect.Iterables.getOnlyElement;
22 
23 import java.io.Serializable;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27 
28 import javax.annotation.Nullable;
29 
30 /**
31  * An immutable, hash-based {@link Map} with reliable user-specified iteration
32  * order. Does not permit null keys or values.
33  *
34  * <p>Unlike {@link Collections#unmodifiableMap}, which is a <i>view</i> of a
35  * separate map which can still change, an instance of {@code ImmutableMap}
36  * contains its own data and will <i>never</i> change. {@code ImmutableMap} is
37  * convenient for {@code public static final} maps ("constant maps") and also
38  * lets you easily make a "defensive copy" of a map provided to your class by a
39  * caller.
40  *
41  * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as
42  * it has no public or protected constructors. Thus, instances of this class are
43  * guaranteed to be immutable.
44  *
45  * @see ImmutableList
46  * @see ImmutableSet
47  * @author Jesse Wilson
48  * @author Kevin Bourrillion
49  * @since 2010.01.04 <b>stable</b> (imported from Google Collections Library)
50  */
51 @GwtCompatible(serializable = true)
52 @SuppressWarnings("serial") // we're overriding default serialization
53 public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
54   /**
55    * Returns the empty map. This map behaves and performs comparably to
56    * {@link Collections#emptyMap}, and is preferable mainly for consistency
57    * and maintainability of your code.
58    */
59   // Casting to any type is safe because the set will never hold any elements.
60   @SuppressWarnings("unchecked")
of()61   public static <K, V> ImmutableMap<K, V> of() {
62     // BEGIN android-changed
63     return (ImmutableMap) EmptyImmutableMap.INSTANCE;
64     // END android-changed
65   }
66 
67   /**
68    * Returns an immutable map containing a single entry. This map behaves and
69    * performs comparably to {@link Collections#singletonMap} but will not accept
70    * a null key or value. It is preferable mainly for consistency and
71    * maintainability of your code.
72    */
of(K k1, V v1)73   public static <K, V> ImmutableMap<K, V> of(K k1, V v1) {
74     return new SingletonImmutableMap<K, V>(
75         checkNotNull(k1), checkNotNull(v1));
76   }
77 
78   /**
79    * Returns an immutable map containing the given entries, in order.
80    *
81    * @throws IllegalArgumentException if duplicate keys are provided
82    */
of(K k1, V v1, K k2, V v2)83   public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2) {
84     return new RegularImmutableMap<K, V>(entryOf(k1, v1), entryOf(k2, v2));
85   }
86 
87   /**
88    * Returns an immutable map containing the given entries, in order.
89    *
90    * @throws IllegalArgumentException if duplicate keys are provided
91    */
of( K k1, V v1, K k2, V v2, K k3, V v3)92   public static <K, V> ImmutableMap<K, V> of(
93       K k1, V v1, K k2, V v2, K k3, V v3) {
94     return new RegularImmutableMap<K, V>(
95         entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3));
96   }
97 
98   /**
99    * Returns an immutable map containing the given entries, in order.
100    *
101    * @throws IllegalArgumentException if duplicate keys are provided
102    */
of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4)103   public static <K, V> ImmutableMap<K, V> of(
104       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
105     return new RegularImmutableMap<K, V>(
106         entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4));
107   }
108 
109   /**
110    * Returns an immutable map containing the given entries, in order.
111    *
112    * @throws IllegalArgumentException if duplicate keys are provided
113    */
of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5)114   public static <K, V> ImmutableMap<K, V> of(
115       K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
116     return new RegularImmutableMap<K, V>(entryOf(k1, v1),
117         entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5));
118   }
119 
120   // looking for of() with > 5 entries? Use the builder instead.
121 
122   /**
123    * Returns a new builder. The generated builder is equivalent to the builder
124    * created by the {@link Builder} constructor.
125    */
builder()126   public static <K, V> Builder<K, V> builder() {
127     return new Builder<K, V>();
128   }
129 
130   /**
131    * Verifies that {@code key} and {@code value} are non-null, and returns a new
132    * immutable entry with those values.
133    *
134    * <p>A call to {@link Map.Entry#setValue} on the returned entry will always
135    * throw {@link UnsupportedOperationException}.
136    */
entryOf(K key, V value)137   static <K, V> Entry<K, V> entryOf(K key, V value) {
138     return Maps.immutableEntry(checkNotNull(key), checkNotNull(value));
139   }
140 
141   /**
142    * A builder for creating immutable map instances, especially {@code public
143    * static final} maps ("constant maps"). Example: <pre>   {@code
144    *
145    *   static final ImmutableMap<String, Integer> WORD_TO_INT =
146    *       new ImmutableMap.Builder<String, Integer>()
147    *           .put("one", 1)
148    *           .put("two", 2)
149    *           .put("three", 3)
150    *           .build();}</pre>
151    *
152    * For <i>small</i> immutable maps, the {@code ImmutableMap.of()} methods are
153    * even more convenient.
154    *
155    * <p>Builder instances can be reused - it is safe to call {@link #build}
156    * multiple times to build multiple maps in series. Each map is a superset of
157    * the maps created before it.
158    */
159   public static class Builder<K, V> {
160     final List<Entry<K, V>> entries = Lists.newArrayList();
161 
162     /**
163      * Creates a new builder. The returned builder is equivalent to the builder
164      * generated by {@link ImmutableMap#builder}.
165      */
Builder()166     public Builder() {}
167 
168     /**
169      * Associates {@code key} with {@code value} in the built map. Duplicate
170      * keys are not allowed, and will cause {@link #build} to fail.
171      */
put(K key, V value)172     public Builder<K, V> put(K key, V value) {
173       entries.add(entryOf(key, value));
174       return this;
175     }
176 
177     /**
178      * Associates all of the given map's keys and values in the built map.
179      * Duplicate keys are not allowed, and will cause {@link #build} to fail.
180      *
181      * @throws NullPointerException if any key or value in {@code map} is null
182      */
putAll(Map<? extends K, ? extends V> map)183     public Builder<K, V> putAll(Map<? extends K, ? extends V> map) {
184       for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
185         put(entry.getKey(), entry.getValue());
186       }
187       return this;
188     }
189 
190     // TODO: Should build() and the ImmutableBiMap & ImmutableSortedMap versions
191     // throw an IllegalStateException instead?
192 
193     /**
194      * Returns a newly-created immutable map.
195      *
196      * @throws IllegalArgumentException if duplicate keys were added
197      */
build()198     public ImmutableMap<K, V> build() {
199       return fromEntryList(entries);
200     }
201 
fromEntryList( List<Entry<K, V>> entries)202     private static <K, V> ImmutableMap<K, V> fromEntryList(
203         List<Entry<K, V>> entries) {
204       int size = entries.size();
205       switch (size) {
206         case 0:
207           return of();
208         case 1:
209           return new SingletonImmutableMap<K, V>(getOnlyElement(entries));
210         default:
211           Entry<?, ?>[] entryArray
212               = entries.toArray(new Entry<?, ?>[entries.size()]);
213           return new RegularImmutableMap<K, V>(entryArray);
214       }
215     }
216   }
217 
218   /**
219    * Returns an immutable map containing the same entries as {@code map}. If
220    * {@code map} somehow contains entries with duplicate keys (for example, if
221    * it is a {@code SortedMap} whose comparator is not <i>consistent with
222    * equals</i>), the results of this method are undefined.
223    *
224    * <p><b>Note:</b> Despite what the method name suggests, if {@code map} is an
225    * {@code ImmutableMap}, no copy will actually be performed, and the given map
226    * itself will be returned.
227    *
228    * @throws NullPointerException if any key or value in {@code map} is null
229    */
copyOf( Map<? extends K, ? extends V> map)230   public static <K, V> ImmutableMap<K, V> copyOf(
231       Map<? extends K, ? extends V> map) {
232     if ((map instanceof ImmutableMap) && !(map instanceof ImmutableSortedMap)) {
233       @SuppressWarnings("unchecked") // safe since map is not writable
234       ImmutableMap<K, V> kvMap = (ImmutableMap<K, V>) map;
235       return kvMap;
236     }
237 
238     @SuppressWarnings("unchecked") // we won't write to this array
239     Entry<K, V>[] entries = map.entrySet().toArray(new Entry[0]);
240     switch (entries.length) {
241       case 0:
242         return of();
243       case 1:
244         return new SingletonImmutableMap<K, V>(entryOf(
245             entries[0].getKey(), entries[0].getValue()));
246       default:
247         for (int i = 0; i < entries.length; i++) {
248           K k = entries[i].getKey();
249           V v = entries[i].getValue();
250           entries[i] = entryOf(k, v);
251         }
252         return new RegularImmutableMap<K, V>(entries);
253     }
254   }
255 
ImmutableMap()256   ImmutableMap() {}
257 
258   /**
259    * Guaranteed to throw an exception and leave the map unmodified.
260    *
261    * @throws UnsupportedOperationException always
262    */
put(K k, V v)263   public final V put(K k, V v) {
264     throw new UnsupportedOperationException();
265   }
266 
267   /**
268    * Guaranteed to throw an exception and leave the map unmodified.
269    *
270    * @throws UnsupportedOperationException always
271    */
remove(Object o)272   public final V remove(Object o) {
273     throw new UnsupportedOperationException();
274   }
275 
276   /**
277    * Guaranteed to throw an exception and leave the map unmodified.
278    *
279    * @throws UnsupportedOperationException always
280    */
putAll(Map<? extends K, ? extends V> map)281   public final void putAll(Map<? extends K, ? extends V> map) {
282     throw new UnsupportedOperationException();
283   }
284 
285   /**
286    * Guaranteed to throw an exception and leave the map unmodified.
287    *
288    * @throws UnsupportedOperationException always
289    */
clear()290   public final void clear() {
291     throw new UnsupportedOperationException();
292   }
293 
isEmpty()294   public boolean isEmpty() {
295     return size() == 0;
296   }
297 
containsKey(@ullable Object key)298   public boolean containsKey(@Nullable Object key) {
299     return get(key) != null;
300   }
301 
302   // Overriding to mark it Nullable
containsValue(@ullable Object value)303   public abstract boolean containsValue(@Nullable Object value);
304 
305   // Overriding to mark it Nullable
get(@ullable Object key)306   public abstract V get(@Nullable Object key);
307 
308   /**
309    * Returns an immutable set of the mappings in this map. The entries are in
310    * the same order as the parameters used to build this map.
311    */
entrySet()312   public abstract ImmutableSet<Entry<K, V>> entrySet();
313 
314   /**
315    * Returns an immutable set of the keys in this map. These keys are in
316    * the same order as the parameters used to build this map.
317    */
keySet()318   public abstract ImmutableSet<K> keySet();
319 
320   /**
321    * Returns an immutable collection of the values in this map. The values are
322    * in the same order as the parameters used to build this map.
323    */
values()324   public abstract ImmutableCollection<V> values();
325 
equals(@ullable Object object)326   @Override public boolean equals(@Nullable Object object) {
327     if (object == this) {
328       return true;
329     }
330     if (object instanceof Map) {
331       Map<?, ?> that = (Map<?, ?>) object;
332       return this.entrySet().equals(that.entrySet());
333     }
334     return false;
335   }
336 
hashCode()337   @Override public int hashCode() {
338     // not caching hash code since it could change if map values are mutable
339     // in a way that modifies their hash codes
340     return entrySet().hashCode();
341   }
342 
toString()343   @Override public String toString() {
344     StringBuilder result = new StringBuilder(size() * 16).append('{');
345     Maps.standardJoiner.appendTo(result, this);
346     return result.append('}').toString();
347   }
348 
349   /**
350    * Serialized type for all ImmutableMap instances. It captures the logical
351    * contents and they are reconstructed using public factory methods. This
352    * ensures that the implementation types remain as implementation details.
353    */
354   static class SerializedForm implements Serializable {
355     private final Object[] keys;
356     private final Object[] values;
SerializedForm(ImmutableMap<?, ?> map)357     SerializedForm(ImmutableMap<?, ?> map) {
358       keys = new Object[map.size()];
359       values = new Object[map.size()];
360       int i = 0;
361       for (Entry<?, ?> entry : map.entrySet()) {
362         keys[i] = entry.getKey();
363         values[i] = entry.getValue();
364         i++;
365       }
366     }
readResolve()367     Object readResolve() {
368       Builder<Object, Object> builder = new Builder<Object, Object>();
369       return createMap(builder);
370     }
createMap(Builder<Object, Object> builder)371     Object createMap(Builder<Object, Object> builder) {
372       for (int i = 0; i < keys.length; i++) {
373         builder.put(keys[i], values[i]);
374       }
375       return builder.build();
376     }
377     private static final long serialVersionUID = 0;
378   }
379 
writeReplace()380   Object writeReplace() {
381     return new SerializedForm(this);
382   }
383 }
384