001/* 002 * Copyright (C) 2008 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; 021 022import com.google.common.annotations.Beta; 023import com.google.common.annotations.GwtCompatible; 024import com.google.common.collect.ImmutableMapEntry.TerminalEntry; 025 026import java.io.Serializable; 027import java.util.Collections; 028import java.util.EnumMap; 029import java.util.HashMap; 030import java.util.Iterator; 031import java.util.Map; 032 033import javax.annotation.Nullable; 034 035/** 036 * An immutable, hash-based {@link Map} with reliable user-specified iteration 037 * order. Does not permit null keys or values. 038 * 039 * <p>Unlike {@link Collections#unmodifiableMap}, which is a <i>view</i> of a 040 * separate map which can still change, an instance of {@code ImmutableMap} 041 * contains its own data and will <i>never</i> change. {@code ImmutableMap} is 042 * convenient for {@code public static final} maps ("constant maps") and also 043 * lets you easily make a "defensive copy" of a map provided to your class by a 044 * caller. 045 * 046 * <p><i>Performance notes:</i> unlike {@link HashMap}, {@code ImmutableMap} is 047 * not optimized for element types that have slow {@link Object#equals} or 048 * {@link Object#hashCode} implementations. You can get better performance by 049 * having your element type cache its own hash codes, and by making use of the 050 * cached values to short-circuit a slow {@code equals} algorithm. 051 * 052 * <p>See the Guava User Guide article on <a href= 053 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> 054 * immutable collections</a>. 055 * 056 * @author Jesse Wilson 057 * @author Kevin Bourrillion 058 * @since 2.0 (imported from Google Collections Library) 059 */ 060@GwtCompatible(serializable = true, emulated = true) 061@SuppressWarnings("serial") // we're overriding default serialization 062public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { 063 064 /** 065 * Returns the empty map. This map behaves and performs comparably to 066 * {@link Collections#emptyMap}, and is preferable mainly for consistency 067 * and maintainability of your code. 068 */ 069 public static <K, V> ImmutableMap<K, V> of() { 070 return ImmutableBiMap.of(); 071 } 072 073 /** 074 * Returns an immutable map containing a single entry. This map behaves and 075 * performs comparably to {@link Collections#singletonMap} but will not accept 076 * a null key or value. It is preferable mainly for consistency and 077 * maintainability of your code. 078 */ 079 public static <K, V> ImmutableMap<K, V> of(K k1, V v1) { 080 return ImmutableBiMap.of(k1, v1); 081 } 082 083 /** 084 * Returns an immutable map containing the given entries, in order. 085 * 086 * @throws IllegalArgumentException if duplicate keys are provided 087 */ 088 public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2) { 089 return new RegularImmutableMap<K, V>(entryOf(k1, v1), entryOf(k2, v2)); 090 } 091 092 /** 093 * Returns an immutable map containing the given entries, in order. 094 * 095 * @throws IllegalArgumentException if duplicate keys are provided 096 */ 097 public static <K, V> ImmutableMap<K, V> of( 098 K k1, V v1, K k2, V v2, K k3, V v3) { 099 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 */ 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 */ 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 */ 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 */ 143 public static <K, V> Builder<K, V> builder() { 144 return new Builder<K, V>(); 145 } 146 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 */ 183 public Builder() { 184 this(ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY); 185 } 186 187 @SuppressWarnings("unchecked") 188 Builder(int initialCapacity) { 189 this.entries = new TerminalEntry[initialCapacity]; 190 this.size = 0; 191 } 192 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 */ 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 */ 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 */ 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 */ 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 */ 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"}) 300 private static <K, V> ImmutableMap<K, V> copyOfEnumMapUnsafe(Map<? extends K, ? extends V> map) { 301 return copyOfEnumMap((EnumMap) map); 302 } 303 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 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 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 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 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 361 public final void clear() { 362 throw new UnsupportedOperationException(); 363 } 364 365 @Override 366 public boolean isEmpty() { 367 return size() == 0; 368 } 369 370 @Override 371 public boolean containsKey(@Nullable Object key) { 372 return get(key) != null; 373 } 374 375 @Override 376 public boolean containsValue(@Nullable Object value) { 377 return values().contains(value); 378 } 379 380 // Overriding to mark it Nullable 381 @Override 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 391 public ImmutableSet<Entry<K, V>> entrySet() { 392 ImmutableSet<Entry<K, V>> result = entrySet; 393 return (result == null) ? entrySet = createEntrySet() : result; 394 } 395 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 405 public ImmutableSet<K> keySet() { 406 ImmutableSet<K> result = keySet; 407 return (result == null) ? keySet = createKeySet() : result; 408 } 409 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 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 435 public ImmutableSetMultimap<K, V> asMultimap() { 436 ImmutableSetMultimap<K, V> result = multimapView; 437 return (result == null) ? (multimapView = createMultimapView()) : result; 438 } 439 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 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 453 MapViewOfValuesAsSingletonSets(ImmutableMap<K, V> delegate) { 454 this.delegate = checkNotNull(delegate); 455 } 456 457 @Override public int size() { 458 return delegate.size(); 459 } 460 461 @Override public boolean containsKey(@Nullable Object key) { 462 return delegate.containsKey(key); 463 } 464 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 470 @Override boolean isPartialView() { 471 return false; 472 } 473 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}