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 017 package com.google.common.collect; 018 019 import static com.google.common.base.Preconditions.checkNotNull; 020 import static com.google.common.collect.Iterables.getOnlyElement; 021 022 import com.google.common.annotations.GwtCompatible; 023 024 import java.io.Serializable; 025 import java.util.ArrayList; 026 import java.util.Collections; 027 import java.util.HashMap; 028 import java.util.List; 029 import java.util.Map; 030 031 import javax.annotation.Nullable; 032 033 /** 034 * An immutable, hash-based {@link Map} with reliable user-specified iteration 035 * order. Does not permit null keys or values. 036 * 037 * <p>Unlike {@link Collections#unmodifiableMap}, which is a <i>view</i> of a 038 * separate map which can still change, an instance of {@code ImmutableMap} 039 * contains its own data and will <i>never</i> change. {@code ImmutableMap} is 040 * convenient for {@code public static final} maps ("constant maps") and also 041 * lets you easily make a "defensive copy" of a map provided to your class by a 042 * caller. 043 * 044 * <p><i>Performance notes:</i> unlike {@link HashMap}, {@code ImmutableMap} is 045 * not optimized for element types that have slow {@link Object#equals} or 046 * {@link Object#hashCode} implementations. You can get better performance by 047 * having your element type cache its own hash codes, and by making use of the 048 * cached values to short-circuit a slow {@code equals} algorithm. 049 * 050 * <p>See the Guava User Guide article on <a href= 051 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> 052 * immutable collections</a>. 053 * 054 * @author Jesse Wilson 055 * @author Kevin Bourrillion 056 * @since 2.0 (imported from Google Collections Library) 057 */ 058 @GwtCompatible(serializable = true, emulated = true) 059 @SuppressWarnings("serial") // we're overriding default serialization 060 public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { 061 /** 062 * Returns the empty map. This map behaves and performs comparably to 063 * {@link Collections#emptyMap}, and is preferable mainly for consistency 064 * and maintainability of your code. 065 */ 066 // Casting to any type is safe because the set will never hold any elements. 067 @SuppressWarnings("unchecked") 068 public static <K, V> ImmutableMap<K, V> of() { 069 return (ImmutableMap<K, V>) EmptyImmutableMap.INSTANCE; 070 } 071 072 /** 073 * Returns an immutable map containing a single entry. This map behaves and 074 * performs comparably to {@link Collections#singletonMap} but will not accept 075 * a null key or value. It is preferable mainly for consistency and 076 * maintainability of your code. 077 */ 078 public static <K, V> ImmutableMap<K, V> of(K k1, V v1) { 079 return new SingletonImmutableMap<K, V>( 080 checkNotNull(k1), checkNotNull(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 SingletonImmutableMap<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 } 278 279 @SuppressWarnings("unchecked") // we won't write to this array 280 Entry<K, V>[] entries = map.entrySet().toArray(new Entry[0]); 281 switch (entries.length) { 282 case 0: 283 return of(); 284 case 1: 285 return new SingletonImmutableMap<K, V>(entryOf( 286 entries[0].getKey(), entries[0].getValue())); 287 default: 288 for (int i = 0; i < entries.length; i++) { 289 K k = entries[i].getKey(); 290 V v = entries[i].getValue(); 291 entries[i] = entryOf(k, v); 292 } 293 return new RegularImmutableMap<K, V>(entries); 294 } 295 } 296 297 ImmutableMap() {} 298 299 /** 300 * Guaranteed to throw an exception and leave the map unmodified. 301 * 302 * @throws UnsupportedOperationException always 303 */ 304 @Override 305 public final V put(K k, V v) { 306 throw new UnsupportedOperationException(); 307 } 308 309 /** 310 * Guaranteed to throw an exception and leave the map unmodified. 311 * 312 * @throws UnsupportedOperationException always 313 */ 314 @Override 315 public final V remove(Object o) { 316 throw new UnsupportedOperationException(); 317 } 318 319 /** 320 * Guaranteed to throw an exception and leave the map unmodified. 321 * 322 * @throws UnsupportedOperationException always 323 */ 324 @Override 325 public final void putAll(Map<? extends K, ? extends V> map) { 326 throw new UnsupportedOperationException(); 327 } 328 329 /** 330 * Guaranteed to throw an exception and leave the map unmodified. 331 * 332 * @throws UnsupportedOperationException always 333 */ 334 @Override 335 public final void clear() { 336 throw new UnsupportedOperationException(); 337 } 338 339 @Override 340 public boolean isEmpty() { 341 return size() == 0; 342 } 343 344 @Override 345 public boolean containsKey(@Nullable Object key) { 346 return get(key) != null; 347 } 348 349 @Override 350 public boolean containsValue(@Nullable Object value) { 351 return value != null && Maps.containsValueImpl(this, value); 352 } 353 354 // Overriding to mark it Nullable 355 @Override 356 public abstract V get(@Nullable Object key); 357 358 private transient ImmutableSet<Entry<K, V>> entrySet; 359 360 /** 361 * Returns an immutable set of the mappings in this map. The entries are in 362 * the same order as the parameters used to build this map. 363 */ 364 @Override 365 public ImmutableSet<Entry<K, V>> entrySet() { 366 ImmutableSet<Entry<K, V>> result = entrySet; 367 return (result == null) ? entrySet = createEntrySet() : result; 368 } 369 370 abstract ImmutableSet<Entry<K, V>> createEntrySet(); 371 372 private transient ImmutableSet<K> keySet; 373 374 /** 375 * Returns an immutable set of the keys in this map. These keys are in 376 * the same order as the parameters used to build this map. 377 */ 378 @Override 379 public ImmutableSet<K> keySet() { 380 ImmutableSet<K> result = keySet; 381 return (result == null) ? keySet = createKeySet() : result; 382 } 383 384 ImmutableSet<K> createKeySet() { 385 return new ImmutableMapKeySet<K, V>(entrySet()) { 386 @Override ImmutableMap<K, V> map() { 387 return ImmutableMap.this; 388 } 389 }; 390 } 391 392 private transient ImmutableCollection<V> values; 393 394 /** 395 * Returns an immutable collection of the values in this map. The values are 396 * in the same order as the parameters used to build this map. 397 */ 398 @Override 399 public ImmutableCollection<V> values() { 400 ImmutableCollection<V> result = values; 401 return (result == null) ? values = createValues() : result; 402 } 403 404 ImmutableCollection<V> createValues() { 405 return new ImmutableMapValues<K, V>() { 406 @Override ImmutableMap<K, V> map() { 407 return ImmutableMap.this; 408 } 409 }; 410 } 411 412 @Override public boolean equals(@Nullable Object object) { 413 return Maps.equalsImpl(this, object); 414 } 415 416 abstract boolean isPartialView(); 417 418 @Override public int hashCode() { 419 // not caching hash code since it could change if map values are mutable 420 // in a way that modifies their hash codes 421 return entrySet().hashCode(); 422 } 423 424 @Override public String toString() { 425 return Maps.toStringImpl(this); 426 } 427 428 /** 429 * Serialized type for all ImmutableMap instances. It captures the logical 430 * contents and they are reconstructed using public factory methods. This 431 * ensures that the implementation types remain as implementation details. 432 */ 433 static class SerializedForm implements Serializable { 434 private final Object[] keys; 435 private final Object[] values; 436 SerializedForm(ImmutableMap<?, ?> map) { 437 keys = new Object[map.size()]; 438 values = new Object[map.size()]; 439 int i = 0; 440 for (Entry<?, ?> entry : map.entrySet()) { 441 keys[i] = entry.getKey(); 442 values[i] = entry.getValue(); 443 i++; 444 } 445 } 446 Object readResolve() { 447 Builder<Object, Object> builder = new Builder<Object, Object>(); 448 return createMap(builder); 449 } 450 Object createMap(Builder<Object, Object> builder) { 451 for (int i = 0; i < keys.length; i++) { 452 builder.put(keys[i], values[i]); 453 } 454 return builder.build(); 455 } 456 private static final long serialVersionUID = 0; 457 } 458 459 Object writeReplace() { 460 return new SerializedForm(this); 461 } 462 }