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