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.Iterables.getOnlyElement; 021 022import com.google.common.annotations.Beta; 023import com.google.common.annotations.GwtCompatible; 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.EnumMap; 029import java.util.HashMap; 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033 034import javax.annotation.Nullable; 035 036/** 037 * An immutable, hash-based {@link Map} with reliable user-specified iteration 038 * order. Does not permit null keys or values. 039 * 040 * <p>Unlike {@link Collections#unmodifiableMap}, which is a <i>view</i> of a 041 * separate map which can still change, an instance of {@code ImmutableMap} 042 * contains its own data and will <i>never</i> change. {@code ImmutableMap} is 043 * convenient for {@code public static final} maps ("constant maps") and also 044 * lets you easily make a "defensive copy" of a map provided to your class by a 045 * caller. 046 * 047 * <p><i>Performance notes:</i> unlike {@link HashMap}, {@code ImmutableMap} is 048 * not optimized for element types that have slow {@link Object#equals} or 049 * {@link Object#hashCode} implementations. You can get better performance by 050 * having your element type cache its own hash codes, and by making use of the 051 * cached values to short-circuit a slow {@code equals} algorithm. 052 * 053 * <p>See the Guava User Guide article on <a href= 054 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> 055 * immutable collections</a>. 056 * 057 * @author Jesse Wilson 058 * @author Kevin Bourrillion 059 * @since 2.0 (imported from Google Collections Library) 060 */ 061@GwtCompatible(serializable = true, emulated = true) 062@SuppressWarnings("serial") // we're overriding default serialization 063public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { 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 * Returns a new builder. The generated builder is equivalent to the builder 129 * created by the {@link Builder} constructor. 130 */ 131 public static <K, V> Builder<K, V> builder() { 132 return new Builder<K, V>(); 133 } 134 135 /** 136 * Verifies that {@code key} and {@code value} are non-null, and returns a new 137 * immutable entry with those values. 138 * 139 * <p>A call to {@link Map.Entry#setValue} on the returned entry will always 140 * throw {@link UnsupportedOperationException}. 141 */ 142 static <K, V> Entry<K, V> entryOf(K key, V value) { 143 checkNotNull(key, "null key in entry: null=%s", value); 144 checkNotNull(value, "null value in entry: %s=null", key); 145 return Maps.immutableEntry(key, value); 146 } 147 148 /** 149 * A builder for creating immutable map instances, especially {@code public 150 * static final} maps ("constant maps"). Example: <pre> {@code 151 * 152 * static final ImmutableMap<String, Integer> WORD_TO_INT = 153 * new ImmutableMap.Builder<String, Integer>() 154 * .put("one", 1) 155 * .put("two", 2) 156 * .put("three", 3) 157 * .build();}</pre> 158 * 159 * For <i>small</i> immutable maps, the {@code ImmutableMap.of()} methods are 160 * even more convenient. 161 * 162 * <p>Builder instances can be reused - it is safe to call {@link #build} 163 * multiple times to build multiple maps in series. Each map is a superset of 164 * the maps created before it. 165 * 166 * @since 2.0 (imported from Google Collections Library) 167 */ 168 public static class Builder<K, V> { 169 final ArrayList<Entry<K, V>> entries = Lists.newArrayList(); 170 171 /** 172 * Creates a new builder. The returned builder is equivalent to the builder 173 * generated by {@link ImmutableMap#builder}. 174 */ 175 public Builder() {} 176 177 /** 178 * Associates {@code key} with {@code value} in the built map. Duplicate 179 * keys are not allowed, and will cause {@link #build} to fail. 180 */ 181 public Builder<K, V> put(K key, V value) { 182 entries.add(entryOf(key, value)); 183 return this; 184 } 185 186 /** 187 * Adds the given {@code entry} to the map, making it immutable if 188 * necessary. Duplicate keys are not allowed, and will cause {@link #build} 189 * to fail. 190 * 191 * @since 11.0 192 */ 193 public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { 194 K key = entry.getKey(); 195 V value = entry.getValue(); 196 if (entry instanceof ImmutableEntry) { 197 checkNotNull(key); 198 checkNotNull(value); 199 @SuppressWarnings("unchecked") // all supported methods are covariant 200 Entry<K, V> immutableEntry = (Entry<K, V>) entry; 201 entries.add(immutableEntry); 202 } else { 203 // Directly calling entryOf(entry.getKey(), entry.getValue()) can cause 204 // compilation error in Eclipse. 205 entries.add(entryOf(key, value)); 206 } 207 return this; 208 } 209 210 /** 211 * Associates all of the given map's keys and values in the built map. 212 * Duplicate keys are not allowed, and will cause {@link #build} to fail. 213 * 214 * @throws NullPointerException if any key or value in {@code map} is null 215 */ 216 public Builder<K, V> putAll(Map<? extends K, ? extends V> map) { 217 entries.ensureCapacity(entries.size() + map.size()); 218 for (Entry<? extends K, ? extends V> entry : map.entrySet()) { 219 put(entry.getKey(), entry.getValue()); 220 } 221 return this; 222 } 223 224 /* 225 * TODO(kevinb): Should build() and the ImmutableBiMap & ImmutableSortedMap 226 * versions throw an IllegalStateException instead? 227 */ 228 229 /** 230 * Returns a newly-created immutable map. 231 * 232 * @throws IllegalArgumentException if duplicate keys were added 233 */ 234 public ImmutableMap<K, V> build() { 235 return fromEntryList(entries); 236 } 237 238 private static <K, V> ImmutableMap<K, V> fromEntryList( 239 List<Entry<K, V>> entries) { 240 int size = entries.size(); 241 switch (size) { 242 case 0: 243 return of(); 244 case 1: 245 return new SingletonImmutableBiMap<K, V>(getOnlyElement(entries)); 246 default: 247 Entry<?, ?>[] entryArray 248 = entries.toArray(new Entry<?, ?>[entries.size()]); 249 return new RegularImmutableMap<K, V>(entryArray); 250 } 251 } 252 } 253 254 /** 255 * Returns an immutable map containing the same entries as {@code map}. If 256 * {@code map} somehow contains entries with duplicate keys (for example, if 257 * it is a {@code SortedMap} whose comparator is not <i>consistent with 258 * equals</i>), the results of this method are undefined. 259 * 260 * <p>Despite the method name, this method attempts to avoid actually copying 261 * the data when it is safe to do so. The exact circumstances under which a 262 * copy will or will not be performed are undocumented and subject to change. 263 * 264 * @throws NullPointerException if any key or value in {@code map} is null 265 */ 266 public static <K, V> ImmutableMap<K, V> copyOf( 267 Map<? extends K, ? extends V> map) { 268 if ((map instanceof ImmutableMap) && !(map instanceof ImmutableSortedMap)) { 269 // TODO(user): Make ImmutableMap.copyOf(immutableBiMap) call copyOf() 270 // on the ImmutableMap delegate(), rather than the bimap itself 271 272 @SuppressWarnings("unchecked") // safe since map is not writable 273 ImmutableMap<K, V> kvMap = (ImmutableMap<K, V>) map; 274 if (!kvMap.isPartialView()) { 275 return kvMap; 276 } 277 } else if (map instanceof EnumMap) { 278 EnumMap<?, ?> enumMap = (EnumMap<?, ?>) map; 279 for (Map.Entry<?, ?> entry : enumMap.entrySet()) { 280 checkNotNull(entry.getKey()); 281 checkNotNull(entry.getValue()); 282 } 283 @SuppressWarnings("unchecked") 284 // immutable collections are safe for covariant casts 285 ImmutableMap<K, V> result = ImmutableEnumMap.asImmutable(new EnumMap(enumMap)); 286 return result; 287 } 288 289 @SuppressWarnings("unchecked") // we won't write to this array 290 Entry<K, V>[] entries = map.entrySet().toArray(new Entry[0]); 291 switch (entries.length) { 292 case 0: 293 return of(); 294 case 1: 295 return new SingletonImmutableBiMap<K, V>(entryOf( 296 entries[0].getKey(), entries[0].getValue())); 297 default: 298 for (int i = 0; i < entries.length; i++) { 299 K k = entries[i].getKey(); 300 V v = entries[i].getValue(); 301 entries[i] = entryOf(k, v); 302 } 303 return new RegularImmutableMap<K, V>(entries); 304 } 305 } 306 307 ImmutableMap() {} 308 309 /** 310 * Guaranteed to throw an exception and leave the map unmodified. 311 * 312 * @throws UnsupportedOperationException always 313 * @deprecated Unsupported operation. 314 */ 315 @Deprecated 316 @Override 317 public final V put(K k, V v) { 318 throw new UnsupportedOperationException(); 319 } 320 321 /** 322 * Guaranteed to throw an exception and leave the map unmodified. 323 * 324 * @throws UnsupportedOperationException always 325 * @deprecated Unsupported operation. 326 */ 327 @Deprecated 328 @Override 329 public final V remove(Object o) { 330 throw new UnsupportedOperationException(); 331 } 332 333 /** 334 * Guaranteed to throw an exception and leave the map unmodified. 335 * 336 * @throws UnsupportedOperationException always 337 * @deprecated Unsupported operation. 338 */ 339 @Deprecated 340 @Override 341 public final void putAll(Map<? extends K, ? extends V> map) { 342 throw new UnsupportedOperationException(); 343 } 344 345 /** 346 * Guaranteed to throw an exception and leave the map unmodified. 347 * 348 * @throws UnsupportedOperationException always 349 * @deprecated Unsupported operation. 350 */ 351 @Deprecated 352 @Override 353 public final void clear() { 354 throw new UnsupportedOperationException(); 355 } 356 357 @Override 358 public boolean isEmpty() { 359 return size() == 0; 360 } 361 362 @Override 363 public boolean containsKey(@Nullable Object key) { 364 return get(key) != null; 365 } 366 367 @Override 368 public boolean containsValue(@Nullable Object value) { 369 return value != null && Maps.containsValueImpl(this, value); 370 } 371 372 // Overriding to mark it Nullable 373 @Override 374 public abstract V get(@Nullable Object key); 375 376 private transient ImmutableSet<Entry<K, V>> entrySet; 377 378 /** 379 * Returns an immutable set of the mappings in this map. The entries are in 380 * the same order as the parameters used to build this map. 381 */ 382 @Override 383 public ImmutableSet<Entry<K, V>> entrySet() { 384 ImmutableSet<Entry<K, V>> result = entrySet; 385 return (result == null) ? entrySet = createEntrySet() : result; 386 } 387 388 abstract ImmutableSet<Entry<K, V>> createEntrySet(); 389 390 private transient ImmutableSet<K> keySet; 391 392 /** 393 * Returns an immutable set of the keys in this map. These keys are in 394 * the same order as the parameters used to build this map. 395 */ 396 @Override 397 public ImmutableSet<K> keySet() { 398 ImmutableSet<K> result = keySet; 399 return (result == null) ? keySet = createKeySet() : result; 400 } 401 402 ImmutableSet<K> createKeySet() { 403 return new ImmutableMapKeySet<K, V>(this); 404 } 405 406 private transient ImmutableCollection<V> values; 407 408 /** 409 * Returns an immutable collection of the values in this map. The values are 410 * in the same order as the parameters used to build this map. 411 */ 412 @Override 413 public ImmutableCollection<V> values() { 414 ImmutableCollection<V> result = values; 415 return (result == null) ? values = new ImmutableMapValues<K, V>(this) : result; 416 } 417 418 // cached so that this.multimapView().inverse() only computes inverse once 419 private transient ImmutableSetMultimap<K, V> multimapView; 420 421 /** 422 * Returns a multimap view of the map. 423 * 424 * @since 14.0 425 */ 426 @Beta 427 public ImmutableSetMultimap<K, V> asMultimap() { 428 ImmutableSetMultimap<K, V> result = multimapView; 429 return (result == null) ? (multimapView = createMultimapView()) : result; 430 } 431 432 private ImmutableSetMultimap<K, V> createMultimapView() { 433 ImmutableMap<K, ImmutableSet<V>> map = viewMapValuesAsSingletonSets(); 434 return new ImmutableSetMultimap<K, V>(map, map.size(), null); 435 } 436 437 private ImmutableMap<K, ImmutableSet<V>> viewMapValuesAsSingletonSets() { 438 class MapViewOfValuesAsSingletonSets extends ImmutableMap<K, ImmutableSet<V>> { 439 @Override public int size() { 440 return ImmutableMap.this.size(); 441 } 442 443 @Override public boolean containsKey(@Nullable Object key) { 444 return ImmutableMap.this.containsKey(key); 445 } 446 447 @Override public ImmutableSet<V> get(@Nullable Object key) { 448 V outerValue = ImmutableMap.this.get(key); 449 return (outerValue == null) ? null : ImmutableSet.of(outerValue); 450 } 451 452 @Override boolean isPartialView() { 453 return false; 454 } 455 456 @Override ImmutableSet<Entry<K, ImmutableSet<V>>> createEntrySet() { 457 return new ImmutableMapEntrySet<K, ImmutableSet<V>>() { 458 @Override ImmutableMap<K, ImmutableSet<V>> map() { 459 return MapViewOfValuesAsSingletonSets.this; 460 } 461 462 @Override 463 public UnmodifiableIterator<Entry<K, ImmutableSet<V>>> iterator() { 464 final Iterator<Entry<K,V>> backingIterator = ImmutableMap.this 465 .entrySet().iterator(); 466 return new UnmodifiableIterator<Entry<K, ImmutableSet<V>>>() { 467 @Override public boolean hasNext() { 468 return backingIterator.hasNext(); 469 } 470 471 @Override public Entry<K, ImmutableSet<V>> next() { 472 final Entry<K, V> backingEntry = backingIterator.next(); 473 return new AbstractMapEntry<K, ImmutableSet<V>>() { 474 @Override public K getKey() { 475 return backingEntry.getKey(); 476 } 477 478 @Override public ImmutableSet<V> getValue() { 479 return ImmutableSet.of(backingEntry.getValue()); 480 } 481 }; 482 } 483 }; 484 } 485 }; 486 } 487 } 488 return new MapViewOfValuesAsSingletonSets(); 489 } 490 491 @Override public boolean equals(@Nullable Object object) { 492 return Maps.equalsImpl(this, object); 493 } 494 495 abstract boolean isPartialView(); 496 497 @Override public int hashCode() { 498 // not caching hash code since it could change if map values are mutable 499 // in a way that modifies their hash codes 500 return entrySet().hashCode(); 501 } 502 503 @Override public String toString() { 504 return Maps.toStringImpl(this); 505 } 506 507 /** 508 * Serialized type for all ImmutableMap instances. It captures the logical 509 * contents and they are reconstructed using public factory methods. This 510 * ensures that the implementation types remain as implementation details. 511 */ 512 static class SerializedForm implements Serializable { 513 private final Object[] keys; 514 private final Object[] values; 515 SerializedForm(ImmutableMap<?, ?> map) { 516 keys = new Object[map.size()]; 517 values = new Object[map.size()]; 518 int i = 0; 519 for (Entry<?, ?> entry : map.entrySet()) { 520 keys[i] = entry.getKey(); 521 values[i] = entry.getValue(); 522 i++; 523 } 524 } 525 Object readResolve() { 526 Builder<Object, Object> builder = new Builder<Object, Object>(); 527 return createMap(builder); 528 } 529 Object createMap(Builder<Object, Object> builder) { 530 for (int i = 0; i < keys.length; i++) { 531 builder.put(keys[i], values[i]); 532 } 533 return builder.build(); 534 } 535 private static final long serialVersionUID = 0; 536 } 537 538 Object writeReplace() { 539 return new SerializedForm(this); 540 } 541}