001 /* 002 * Copyright (C) 2007 Google Inc. 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.checkArgument; 020 import static com.google.common.base.Preconditions.checkNotNull; 021 022 import com.google.common.annotations.GwtCompatible; 023 import com.google.common.base.Function; 024 import com.google.common.base.Joiner.MapJoiner; 025 import com.google.common.base.Objects; 026 import com.google.common.base.Predicate; 027 import com.google.common.base.Predicates; 028 029 import java.io.Serializable; 030 import java.util.AbstractCollection; 031 import java.util.AbstractMap; 032 import java.util.AbstractSet; 033 import java.util.Collection; 034 import java.util.Collections; 035 import java.util.Comparator; 036 import java.util.EnumMap; 037 import java.util.Enumeration; 038 import java.util.HashMap; 039 import java.util.IdentityHashMap; 040 import java.util.Iterator; 041 import java.util.LinkedHashMap; 042 import java.util.Map; 043 import java.util.Map.Entry; 044 import java.util.Properties; 045 import java.util.Set; 046 import java.util.SortedMap; 047 import java.util.TreeMap; 048 import java.util.concurrent.ConcurrentMap; 049 050 import javax.annotation.Nullable; 051 052 /** 053 * Static utility methods pertaining to {@link Map} instances. Also see this 054 * class's counterparts {@link Lists} and {@link Sets}. 055 * 056 * @author Kevin Bourrillion 057 * @author Mike Bostock 058 * @author Isaac Shum 059 * @since 2 (imported from Google Collections Library) 060 */ 061 @GwtCompatible 062 public final class Maps { 063 private Maps() {} 064 065 /** 066 * Creates a <i>mutable</i>, empty {@code HashMap} instance. 067 * 068 * <p><b>Note:</b> if mutability is not required, use {@link 069 * ImmutableMap#of()} instead. 070 * 071 * <p><b>Note:</b> if {@code K} is an {@code enum} type, use {@link 072 * #newEnumMap} instead. 073 * 074 * @return a new, empty {@code HashMap} 075 */ 076 public static <K, V> HashMap<K, V> newHashMap() { 077 return new HashMap<K, V>(); 078 } 079 080 /** 081 * Creates a {@code HashMap} instance with enough capacity to hold the 082 * specified number of elements without rehashing. 083 * 084 * @param expectedSize the expected size 085 * @return a new, empty {@code HashMap} with enough capacity to hold {@code 086 * expectedSize} elements without rehashing 087 * @throws IllegalArgumentException if {@code expectedSize} is negative 088 */ 089 public static <K, V> HashMap<K, V> newHashMapWithExpectedSize( 090 int expectedSize) { 091 /* 092 * The HashMap is constructed with an initialCapacity that's greater than 093 * expectedSize. The larger value is necessary because HashMap resizes its 094 * internal array if the map size exceeds loadFactor * initialCapacity. 095 */ 096 return new HashMap<K, V>(capacity(expectedSize)); 097 } 098 099 /** 100 * Returns an appropriate value for the "capacity" (in reality, "minimum table 101 * size") parameter of a {@link HashMap} constructor, such that the resulting 102 * table will be between 25% and 50% full when it contains {@code 103 * expectedSize} entries. 104 * 105 * @throws IllegalArgumentException if {@code expectedSize} is negative 106 */ 107 static int capacity(int expectedSize) { 108 checkArgument(expectedSize >= 0); 109 return Math.max(expectedSize * 2, 16); 110 } 111 112 /** 113 * Creates a <i>mutable</i> {@code HashMap} instance with the same mappings as 114 * the specified map. 115 * 116 * <p><b>Note:</b> if mutability is not required, use {@link 117 * ImmutableMap#copyOf(Map)} instead. 118 * 119 * <p><b>Note:</b> if {@code K} is an {@link Enum} type, use {@link 120 * #newEnumMap} instead. 121 * 122 * @param map the mappings to be placed in the new map 123 * @return a new {@code HashMap} initialized with the mappings from {@code 124 * map} 125 */ 126 public static <K, V> HashMap<K, V> newHashMap( 127 Map<? extends K, ? extends V> map) { 128 return new HashMap<K, V>(map); 129 } 130 131 /** 132 * Creates a <i>mutable</i>, empty, insertion-ordered {@code LinkedHashMap} 133 * instance. 134 * 135 * <p><b>Note:</b> if mutability is not required, use {@link 136 * ImmutableMap#of()} instead. 137 * 138 * @return a new, empty {@code LinkedHashMap} 139 */ 140 public static <K, V> LinkedHashMap<K, V> newLinkedHashMap() { 141 return new LinkedHashMap<K, V>(); 142 } 143 144 /** 145 * Creates a <i>mutable</i>, insertion-ordered {@code LinkedHashMap} instance 146 * with the same mappings as the specified map. 147 * 148 * <p><b>Note:</b> if mutability is not required, use {@link 149 * ImmutableMap#copyOf(Map)} instead. 150 * 151 * @param map the mappings to be placed in the new map 152 * @return a new, {@code LinkedHashMap} initialized with the mappings from 153 * {@code map} 154 */ 155 public static <K, V> LinkedHashMap<K, V> newLinkedHashMap( 156 Map<? extends K, ? extends V> map) { 157 return new LinkedHashMap<K, V>(map); 158 } 159 160 /** 161 * Returns a general-purpose instance of {@code ConcurrentMap}, which supports 162 * all optional operations of the ConcurrentMap interface. It does not permit 163 * null keys or values. It is serializable. 164 * 165 * <p>This is currently accomplished by calling {@link MapMaker#makeMap()}. 166 * 167 * <p>It is preferable to use {@code MapMaker} directly (rather than through 168 * this method), as it presents numerous useful configuration options, 169 * such as the concurrency level, load factor, key/value reference types, 170 * and value computation. 171 * 172 * @return a new, empty {@code ConcurrentMap} 173 * @since 3 174 */ 175 public static <K, V> ConcurrentMap<K, V> newConcurrentMap() { 176 return new MapMaker().<K, V>makeMap(); 177 } 178 179 /** 180 * Creates a <i>mutable</i>, empty {@code TreeMap} instance using the natural 181 * ordering of its elements. 182 * 183 * <p><b>Note:</b> if mutability is not required, use {@link 184 * ImmutableSortedMap#of()} instead. 185 * 186 * @return a new, empty {@code TreeMap} 187 */ 188 @SuppressWarnings("unchecked") // eclipse doesn't like the raw Comparable 189 public static <K extends Comparable, V> TreeMap<K, V> newTreeMap() { 190 return new TreeMap<K, V>(); 191 } 192 193 /** 194 * Creates a <i>mutable</i> {@code TreeMap} instance with the same mappings as 195 * the specified map and using the same ordering as the specified map. 196 * 197 * <p><b>Note:</b> if mutability is not required, use {@link 198 * ImmutableSortedMap#copyOfSorted(SortedMap)} instead. 199 * 200 * @param map the sorted map whose mappings are to be placed in the new map 201 * and whose comparator is to be used to sort the new map 202 * @return a new {@code TreeMap} initialized with the mappings from {@code 203 * map} and using the comparator of {@code map} 204 */ 205 public static <K, V> TreeMap<K, V> newTreeMap(SortedMap<K, ? extends V> map) { 206 return new TreeMap<K, V>(map); 207 } 208 209 /** 210 * Creates a <i>mutable</i>, empty {@code TreeMap} instance using the given 211 * comparator. 212 * 213 * <p><b>Note:</b> if mutability is not required, use {@code 214 * ImmutableSortedMap.orderedBy(comparator).build()} instead. 215 * 216 * @param comparator the comparator to sort the keys with 217 * @return a new, empty {@code TreeMap} 218 */ 219 public static <C, K extends C, V> TreeMap<K, V> newTreeMap( 220 @Nullable Comparator<C> comparator) { 221 // Ideally, the extra type parameter "C" shouldn't be necessary. It is a 222 // work-around of a compiler type inference quirk that prevents the 223 // following code from being compiled: 224 // Comparator<Class<?>> comparator = null; 225 // Map<Class<? extends Throwable>, String> map = newTreeMap(comparator); 226 return new TreeMap<K, V>(comparator); 227 } 228 229 /** 230 * Creates an {@code EnumMap} instance. 231 * 232 * @param type the key type for this map 233 * @return a new, empty {@code EnumMap} 234 */ 235 public static <K extends Enum<K>, V> EnumMap<K, V> newEnumMap(Class<K> type) { 236 return new EnumMap<K, V>(checkNotNull(type)); 237 } 238 239 /** 240 * Creates an {@code EnumMap} with the same mappings as the specified map. 241 * 242 * @param map the map from which to initialize this {@code EnumMap} 243 * @return a new {@code EnumMap} initialized with the mappings from {@code 244 * map} 245 * @throws IllegalArgumentException if {@code m} is not an {@code EnumMap} 246 * instance and contains no mappings 247 */ 248 public static <K extends Enum<K>, V> EnumMap<K, V> newEnumMap( 249 Map<K, ? extends V> map) { 250 return new EnumMap<K, V>(map); 251 } 252 253 /** 254 * Creates an {@code IdentityHashMap} instance. 255 * 256 * @return a new, empty {@code IdentityHashMap} 257 */ 258 public static <K, V> IdentityHashMap<K, V> newIdentityHashMap() { 259 return new IdentityHashMap<K, V>(); 260 } 261 262 /** 263 * Returns a synchronized (thread-safe) bimap backed by the specified bimap. 264 * In order to guarantee serial access, it is critical that <b>all</b> access 265 * to the backing bimap is accomplished through the returned bimap. 266 * 267 * <p>It is imperative that the user manually synchronize on the returned map 268 * when accessing any of its collection views: <pre> {@code 269 * 270 * BiMap<Long, String> map = Maps.synchronizedBiMap( 271 * HashBiMap.<Long, String>create()); 272 * ... 273 * Set<Long> set = map.keySet(); // Needn't be in synchronized block 274 * ... 275 * synchronized (map) { // Synchronizing on map, not set! 276 * Iterator<Long> it = set.iterator(); // Must be in synchronized block 277 * while (it.hasNext()) { 278 * foo(it.next()); 279 * } 280 * }}</pre> 281 * 282 * Failure to follow this advice may result in non-deterministic behavior. 283 * 284 * <p>The returned bimap will be serializable if the specified bimap is 285 * serializable. 286 * 287 * @param bimap the bimap to be wrapped in a synchronized view 288 * @return a sychronized view of the specified bimap 289 */ 290 public static <K, V> BiMap<K, V> synchronizedBiMap(BiMap<K, V> bimap) { 291 return Synchronized.biMap(bimap, null); 292 } 293 294 /** 295 * Computes the difference between two maps. This difference is an immutable 296 * snapshot of the state of the maps at the time this method is called. It 297 * will never change, even if the maps change at a later time. 298 * 299 * <p>Since this method uses {@code HashMap} instances internally, the keys of 300 * the supplied maps must be well-behaved with respect to 301 * {@link Object#equals} and {@link Object#hashCode}. 302 * 303 * <p><b>Note:</b>If you only need to know whether two maps have the same 304 * mappings, call {@code left.equals(right)} instead of this method. 305 * 306 * @param left the map to treat as the "left" map for purposes of comparison 307 * @param right the map to treat as the "right" map for purposes of comparison 308 * @return the difference between the two maps 309 */ 310 public static <K, V> MapDifference<K, V> difference( 311 Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right) { 312 Map<K, V> onlyOnLeft = newHashMap(); 313 Map<K, V> onlyOnRight = new HashMap<K, V>(right); // will whittle it down 314 Map<K, V> onBoth = newHashMap(); 315 Map<K, MapDifference.ValueDifference<V>> differences = newHashMap(); 316 boolean eq = true; 317 318 for (Entry<? extends K, ? extends V> entry : left.entrySet()) { 319 K leftKey = entry.getKey(); 320 V leftValue = entry.getValue(); 321 if (right.containsKey(leftKey)) { 322 V rightValue = onlyOnRight.remove(leftKey); 323 if (Objects.equal(leftValue, rightValue)) { 324 onBoth.put(leftKey, leftValue); 325 } else { 326 eq = false; 327 differences.put( 328 leftKey, new ValueDifferenceImpl<V>(leftValue, rightValue)); 329 } 330 } else { 331 eq = false; 332 onlyOnLeft.put(leftKey, leftValue); 333 } 334 } 335 336 boolean areEqual = eq && onlyOnRight.isEmpty(); 337 return new MapDifferenceImpl<K, V>( 338 areEqual, onlyOnLeft, onlyOnRight, onBoth, differences); 339 } 340 341 private static class MapDifferenceImpl<K, V> implements MapDifference<K, V> { 342 final boolean areEqual; 343 final Map<K, V> onlyOnLeft; 344 final Map<K, V> onlyOnRight; 345 final Map<K, V> onBoth; 346 final Map<K, ValueDifference<V>> differences; 347 348 MapDifferenceImpl(boolean areEqual, Map<K, V> onlyOnLeft, 349 Map<K, V> onlyOnRight, Map<K, V> onBoth, 350 Map<K, ValueDifference<V>> differences) { 351 this.areEqual = areEqual; 352 this.onlyOnLeft = Collections.unmodifiableMap(onlyOnLeft); 353 this.onlyOnRight = Collections.unmodifiableMap(onlyOnRight); 354 this.onBoth = Collections.unmodifiableMap(onBoth); 355 this.differences = Collections.unmodifiableMap(differences); 356 } 357 358 public boolean areEqual() { 359 return areEqual; 360 } 361 362 public Map<K, V> entriesOnlyOnLeft() { 363 return onlyOnLeft; 364 } 365 366 public Map<K, V> entriesOnlyOnRight() { 367 return onlyOnRight; 368 } 369 370 public Map<K, V> entriesInCommon() { 371 return onBoth; 372 } 373 374 public Map<K, ValueDifference<V>> entriesDiffering() { 375 return differences; 376 } 377 378 @Override public boolean equals(Object object) { 379 if (object == this) { 380 return true; 381 } 382 if (object instanceof MapDifference) { 383 MapDifference<?, ?> other = (MapDifference<?, ?>) object; 384 return entriesOnlyOnLeft().equals(other.entriesOnlyOnLeft()) 385 && entriesOnlyOnRight().equals(other.entriesOnlyOnRight()) 386 && entriesInCommon().equals(other.entriesInCommon()) 387 && entriesDiffering().equals(other.entriesDiffering()); 388 } 389 return false; 390 } 391 392 @Override public int hashCode() { 393 return Objects.hashCode(entriesOnlyOnLeft(), entriesOnlyOnRight(), 394 entriesInCommon(), entriesDiffering()); 395 } 396 397 @Override public String toString() { 398 if (areEqual) { 399 return "equal"; 400 } 401 402 StringBuilder result = new StringBuilder("not equal"); 403 if (!onlyOnLeft.isEmpty()) { 404 result.append(": only on left=").append(onlyOnLeft); 405 } 406 if (!onlyOnRight.isEmpty()) { 407 result.append(": only on right=").append(onlyOnRight); 408 } 409 if (!differences.isEmpty()) { 410 result.append(": value differences=").append(differences); 411 } 412 return result.toString(); 413 } 414 } 415 416 static class ValueDifferenceImpl<V> 417 implements MapDifference.ValueDifference<V> { 418 private final V left; 419 private final V right; 420 421 ValueDifferenceImpl(@Nullable V left, @Nullable V right) { 422 this.left = left; 423 this.right = right; 424 } 425 426 public V leftValue() { 427 return left; 428 } 429 430 public V rightValue() { 431 return right; 432 } 433 434 @Override public boolean equals(@Nullable Object object) { 435 if (object instanceof MapDifference.ValueDifference<?>) { 436 MapDifference.ValueDifference<?> that = 437 (MapDifference.ValueDifference<?>) object; 438 return Objects.equal(this.left, that.leftValue()) 439 && Objects.equal(this.right, that.rightValue()); 440 } 441 return false; 442 } 443 444 @Override public int hashCode() { 445 return Objects.hashCode(left, right); 446 } 447 448 @Override public String toString() { 449 return "(" + left + ", " + right + ")"; 450 } 451 } 452 453 /** 454 * Returns an immutable map for which the {@link Map#values} are the given 455 * elements in the given order, and each key is the product of invoking a 456 * supplied function on its corresponding value. 457 * 458 * @param values the values to use when constructing the {@code Map} 459 * @param keyFunction the function used to produce the key for each value 460 * @return a map mapping the result of evaluating the function {@code 461 * keyFunction} on each value in the input collection to that value 462 * @throws IllegalArgumentException if {@code keyFunction} produces the same 463 * key for more than one value in the input collection 464 * @throws NullPointerException if any elements of {@code values} is null, or 465 * if {@code keyFunction} produces {@code null} for any value 466 */ 467 // TODO: consider returning a bimap, whose inverse view does lookups by 468 // invoking the function. 469 public static <K, V> ImmutableMap<K, V> uniqueIndex( 470 Iterable<V> values, Function<? super V, K> keyFunction) { 471 checkNotNull(keyFunction); 472 ImmutableMap.Builder<K, V> builder = ImmutableMap.builder(); 473 for (V value : values) { 474 builder.put(keyFunction.apply(value), value); 475 } 476 return builder.build(); 477 } 478 479 /** 480 * Creates an {@code ImmutableMap<String, String>} from a {@code Properties} 481 * instance. Properties normally derive from {@code Map<Object, Object>}, but 482 * they typically contain strings, which is awkward. This method lets you get 483 * a plain-old-{@code Map} out of a {@code Properties}. 484 * 485 * @param properties a {@code Properties} object to be converted 486 * @return an immutable map containing all the entries in {@code properties} 487 * @throws ClassCastException if any key in {@code Properties} is not a {@code 488 * String} 489 * @throws NullPointerException if any key or value in {@code Properties} is 490 * null 491 */ 492 public static ImmutableMap<String, String> fromProperties( 493 Properties properties) { 494 ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); 495 496 for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();) { 497 String key = (String) e.nextElement(); 498 builder.put(key, properties.getProperty(key)); 499 } 500 501 return builder.build(); 502 } 503 504 /** 505 * Returns an immutable map entry with the specified key and value. The {@link 506 * Entry#setValue} operation throws an {@link UnsupportedOperationException}. 507 * 508 * <p>The returned entry is serializable. 509 * 510 * @param key the key to be associated with the returned entry 511 * @param value the value to be associated with the returned entry 512 */ 513 public static <K, V> Entry<K, V> immutableEntry( 514 @Nullable K key, @Nullable V value) { 515 return new ImmutableEntry<K, V>(key, value); 516 } 517 518 /** 519 * Returns an unmodifiable view of the specified set of entries. The {@link 520 * Entry#setValue} operation throws an {@link UnsupportedOperationException}, 521 * as do any operations that would modify the returned set. 522 * 523 * @param entrySet the entries for which to return an unmodifiable view 524 * @return an unmodifiable view of the entries 525 */ 526 static <K, V> Set<Entry<K, V>> unmodifiableEntrySet( 527 Set<Entry<K, V>> entrySet) { 528 return new UnmodifiableEntrySet<K, V>( 529 Collections.unmodifiableSet(entrySet)); 530 } 531 532 /** 533 * Returns an unmodifiable view of the specified map entry. The {@link 534 * Entry#setValue} operation throws an {@link UnsupportedOperationException}. 535 * This also has the side-effect of redefining {@code equals} to comply with 536 * the Entry contract, to avoid a possible nefarious implementation of equals. 537 * 538 * @param entry the entry for which to return an unmodifiable view 539 * @return an unmodifiable view of the entry 540 */ 541 private static <K, V> Entry<K, V> unmodifiableEntry(final Entry<K, V> entry) { 542 checkNotNull(entry); 543 return new AbstractMapEntry<K, V>() { 544 @Override public K getKey() { 545 return entry.getKey(); 546 } 547 548 @Override public V getValue() { 549 return entry.getValue(); 550 } 551 }; 552 } 553 554 /** @see Multimaps#unmodifiableEntries */ 555 static class UnmodifiableEntries<K, V> 556 extends ForwardingCollection<Entry<K, V>> { 557 private final Collection<Entry<K, V>> entries; 558 559 UnmodifiableEntries(Collection<Entry<K, V>> entries) { 560 this.entries = entries; 561 } 562 563 @Override protected Collection<Entry<K, V>> delegate() { 564 return entries; 565 } 566 567 @Override public Iterator<Entry<K, V>> iterator() { 568 final Iterator<Entry<K, V>> delegate = super.iterator(); 569 return new ForwardingIterator<Entry<K, V>>() { 570 @Override public Entry<K, V> next() { 571 return unmodifiableEntry(super.next()); 572 } 573 574 @Override protected Iterator<Entry<K, V>> delegate() { 575 return delegate; 576 } 577 }; 578 } 579 580 // See java.util.Collections.UnmodifiableEntrySet for details on attacks. 581 582 @Override public Object[] toArray() { 583 return ObjectArrays.toArrayImpl(this); 584 } 585 586 @Override public <T> T[] toArray(T[] array) { 587 return ObjectArrays.toArrayImpl(this, array); 588 } 589 590 @Override public boolean contains(Object o) { 591 return containsEntryImpl(delegate(), o); 592 } 593 594 @Override public boolean containsAll(Collection<?> c) { 595 return Collections2.containsAll(this, c); 596 } 597 } 598 599 /** @see Maps#unmodifiableEntrySet(Set) */ 600 static class UnmodifiableEntrySet<K, V> 601 extends UnmodifiableEntries<K, V> implements Set<Entry<K, V>> { 602 UnmodifiableEntrySet(Set<Entry<K, V>> entries) { 603 super(entries); 604 } 605 606 // See java.util.Collections.UnmodifiableEntrySet for details on attacks. 607 608 @Override public boolean equals(@Nullable Object object) { 609 return Collections2.setEquals(this, object); 610 } 611 612 @Override public int hashCode() { 613 return Sets.hashCodeImpl(this); 614 } 615 } 616 617 /** 618 * Returns an unmodifiable view of the specified bimap. This method allows 619 * modules to provide users with "read-only" access to internal bimaps. Query 620 * operations on the returned bimap "read through" to the specified bimap, and 621 * attemps to modify the returned map, whether direct or via its collection 622 * views, result in an {@code UnsupportedOperationException}. 623 * 624 * <p>The returned bimap will be serializable if the specified bimap is 625 * serializable. 626 * 627 * @param bimap the bimap for which an unmodifiable view is to be returned 628 * @return an unmodifiable view of the specified bimap 629 */ 630 public static <K, V> BiMap<K, V> unmodifiableBiMap( 631 BiMap<? extends K, ? extends V> bimap) { 632 return new UnmodifiableBiMap<K, V>(bimap, null); 633 } 634 635 /** @see Maps#unmodifiableBiMap(BiMap) */ 636 private static class UnmodifiableBiMap<K, V> 637 extends ForwardingMap<K, V> implements BiMap<K, V>, Serializable { 638 final Map<K, V> unmodifiableMap; 639 final BiMap<? extends K, ? extends V> delegate; 640 transient BiMap<V, K> inverse; 641 transient Set<V> values; 642 643 UnmodifiableBiMap(BiMap<? extends K, ? extends V> delegate, 644 @Nullable BiMap<V, K> inverse) { 645 unmodifiableMap = Collections.unmodifiableMap(delegate); 646 this.delegate = delegate; 647 this.inverse = inverse; 648 } 649 650 @Override protected Map<K, V> delegate() { 651 return unmodifiableMap; 652 } 653 654 public V forcePut(K key, V value) { 655 throw new UnsupportedOperationException(); 656 } 657 658 public BiMap<V, K> inverse() { 659 BiMap<V, K> result = inverse; 660 return (result == null) 661 ? inverse = new UnmodifiableBiMap<V, K>(delegate.inverse(), this) 662 : result; 663 } 664 665 @Override public Set<V> values() { 666 Set<V> result = values; 667 return (result == null) 668 ? values = Collections.unmodifiableSet(delegate.values()) 669 : result; 670 } 671 672 private static final long serialVersionUID = 0; 673 } 674 675 /** 676 * Implements {@code Collection.contains} safely for forwarding collections of 677 * map entries. If {@code o} is an instance of {@code Map.Entry}, it is 678 * wrapped using {@link #unmodifiableEntry} to protect against a possible 679 * nefarious equals method. 680 * 681 * <p>Note that {@code c} is the backing (delegate) collection, rather than 682 * the forwarding collection. 683 * 684 * @param c the delegate (unwrapped) collection of map entries 685 * @param o the object that might be contained in {@code c} 686 * @return {@code true} if {@code c} contains {@code o} 687 */ 688 static <K, V> boolean containsEntryImpl(Collection<Entry<K, V>> c, Object o) { 689 if (!(o instanceof Entry)) { 690 return false; 691 } 692 return c.contains(unmodifiableEntry((Entry<?, ?>) o)); 693 } 694 695 /** 696 * Implements {@code Collection.remove} safely for forwarding collections of 697 * map entries. If {@code o} is an instance of {@code Map.Entry}, it is 698 * wrapped using {@link #unmodifiableEntry} to protect against a possible 699 * nefarious equals method. 700 * 701 * <p>Note that {@code c} is backing (delegate) collection, rather than the 702 * forwarding collection. 703 * 704 * @param c the delegate (unwrapped) collection of map entries 705 * @param o the object to remove from {@code c} 706 * @return {@code true} if {@code c} was changed 707 */ 708 static <K, V> boolean removeEntryImpl(Collection<Entry<K, V>> c, Object o) { 709 if (!(o instanceof Entry)) { 710 return false; 711 } 712 return c.remove(unmodifiableEntry((Entry<?, ?>) o)); 713 } 714 715 /** 716 * Returns a view of a map where each value is transformed by a function. All 717 * other properties of the map, such as iteration order, are left intact. For 718 * example, the code: <pre> {@code 719 * 720 * Map<String, Integer> map = ImmutableMap.of("a", 4, "b", 9); 721 * Function<Integer, Double> sqrt = 722 * new Function<Integer, Double>() { 723 * public Double apply(Integer in) { 724 * return Math.sqrt((int) in); 725 * } 726 * }; 727 * Map<String, Double> transformed = Maps.transformValues(map, sqrt); 728 * System.out.println(transformed);}</pre> 729 * 730 * ... prints {@code {a=2.0, b=3.0}}. 731 * 732 * <p>Changes in the underlying map are reflected in this view. Conversely, 733 * this view supports removal operations, and these are reflected in the 734 * underlying map. 735 * 736 * <p>It's acceptable for the underlying map to contain null keys, and even 737 * null values provided that the function is capable of accepting null input. 738 * The transformed map might contain null values, if the function sometimes 739 * gives a null result. 740 * 741 * <p>The returned map is not thread-safe or serializable, even if the 742 * underlying map is. 743 * 744 * <p>The function is applied lazily, invoked when needed. This is necessary 745 * for the returned map to be a view, but it means that the function will be 746 * applied many times for bulk operations like {@link Map#containsValue} and 747 * {@code Map.toString()}. For this to perform well, {@code function} should 748 * be fast. To avoid lazy evaluation when the returned map doesn't need to be 749 * a view, copy the returned map into a new map of your choosing. 750 */ 751 public static <K, V1, V2> Map<K, V2> transformValues( 752 Map<K, V1> fromMap, Function<? super V1, V2> function) { 753 return new TransformedValuesMap<K, V1, V2>(fromMap, function); 754 } 755 756 private static class TransformedValuesMap<K, V1, V2> 757 extends AbstractMap<K, V2> { 758 final Map<K, V1> fromMap; 759 final Function<? super V1, V2> function; 760 761 TransformedValuesMap( 762 Map<K, V1> fromMap, Function<? super V1, V2> function) { 763 this.fromMap = checkNotNull(fromMap); 764 this.function = checkNotNull(function); 765 } 766 767 @Override public int size() { 768 return fromMap.size(); 769 } 770 771 @Override public boolean containsKey(Object key) { 772 return fromMap.containsKey(key); 773 } 774 775 @Override public V2 get(Object key) { 776 V1 value = fromMap.get(key); 777 return (value != null || fromMap.containsKey(key)) 778 ? function.apply(value) 779 : null; 780 } 781 782 @Override public V2 remove(Object key) { 783 return fromMap.containsKey(key) 784 ? function.apply(fromMap.remove(key)) 785 : null; 786 } 787 788 @Override public void clear() { 789 fromMap.clear(); 790 } 791 792 EntrySet entrySet; 793 794 @Override public Set<Entry<K, V2>> entrySet() { 795 EntrySet result = entrySet; 796 if (result == null) { 797 entrySet = result = new EntrySet(); 798 } 799 return result; 800 } 801 802 class EntrySet extends AbstractSet<Entry<K, V2>> { 803 @Override public int size() { 804 return TransformedValuesMap.this.size(); 805 } 806 807 @Override public Iterator<Entry<K, V2>> iterator() { 808 final Iterator<Entry<K, V1>> mapIterator = 809 fromMap.entrySet().iterator(); 810 811 return new Iterator<Entry<K, V2>>() { 812 public boolean hasNext() { 813 return mapIterator.hasNext(); 814 } 815 816 public Entry<K, V2> next() { 817 final Entry<K, V1> entry = mapIterator.next(); 818 return new AbstractMapEntry<K, V2>() { 819 @Override public K getKey() { 820 return entry.getKey(); 821 } 822 823 @Override public V2 getValue() { 824 return function.apply(entry.getValue()); 825 } 826 }; 827 } 828 829 public void remove() { 830 mapIterator.remove(); 831 } 832 }; 833 } 834 835 @Override public void clear() { 836 fromMap.clear(); 837 } 838 839 @Override public boolean contains(Object o) { 840 if (!(o instanceof Entry)) { 841 return false; 842 } 843 Entry<?, ?> entry = (Entry<?, ?>) o; 844 Object entryKey = entry.getKey(); 845 Object entryValue = entry.getValue(); 846 V2 mapValue = TransformedValuesMap.this.get(entryKey); 847 if (mapValue != null) { 848 return mapValue.equals(entryValue); 849 } 850 return entryValue == null && containsKey(entryKey); 851 } 852 853 @Override public boolean remove(Object o) { 854 if (contains(o)) { 855 Entry<?, ?> entry = (Entry<?, ?>) o; 856 Object key = entry.getKey(); 857 fromMap.remove(key); 858 return true; 859 } 860 return false; 861 } 862 } 863 } 864 865 /** 866 * Returns a map containing the mappings in {@code unfiltered} whose keys 867 * satisfy a predicate. The returned map is a live view of {@code unfiltered}; 868 * changes to one affect the other. 869 * 870 * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code 871 * values()} views have iterators that don't support {@code remove()}, but all 872 * other methods are supported by the map and its views. The map's {@code 873 * put()} and {@code putAll()} methods throw an {@link 874 * IllegalArgumentException} if a key that doesn't satisfy the predicate is 875 * provided. 876 * 877 * <p>When methods such as {@code removeAll()} and {@code clear()} are called 878 * on the filtered map or its views, only mappings whose keys satisfy the 879 * filter will be removed from the underlying map. 880 * 881 * <p>The returned map isn't threadsafe or serializable, even if {@code 882 * unfiltered} is. 883 * 884 * <p>Many of the filtered map's methods, such as {@code size()}, 885 * iterate across every key/value mapping in the underlying map and determine 886 * which satisfy the filter. When a live view is <i>not</i> needed, it may be 887 * faster to copy the filtered map and use the copy. 888 */ 889 public static <K, V> Map<K, V> filterKeys( 890 Map<K, V> unfiltered, final Predicate<? super K> keyPredicate) { 891 checkNotNull(keyPredicate); 892 Predicate<Entry<K, V>> entryPredicate = 893 new Predicate<Entry<K, V>>() { 894 public boolean apply(Entry<K, V> input) { 895 return keyPredicate.apply(input.getKey()); 896 } 897 }; 898 return (unfiltered instanceof AbstractFilteredMap) 899 ? filterFiltered((AbstractFilteredMap<K, V>) unfiltered, entryPredicate) 900 : new FilteredKeyMap<K, V>( 901 checkNotNull(unfiltered), keyPredicate, entryPredicate); 902 } 903 904 /** 905 * Returns a map containing the mappings in {@code unfiltered} whose values 906 * satisfy a predicate. The returned map is a live view of {@code unfiltered}; 907 * changes to one affect the other. 908 * 909 * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code 910 * values()} views have iterators that don't support {@code remove()}, but all 911 * other methods are supported by the map and its views. The {@link Map#put}, 912 * {@link Map#putAll}, and {@link Entry#setValue} methods throw an {@link 913 * IllegalArgumentException} if a value that doesn't satisfy the predicate is 914 * provided. 915 * 916 * <p>When methods such as {@code removeAll()} and {@code clear()} are called 917 * on the filtered map or its views, only mappings whose values satisfy the 918 * filter will be removed from the underlying map. 919 * 920 * <p>The returned map isn't threadsafe or serializable, even if {@code 921 * unfiltered} is. 922 * 923 * <p>Many of the filtered map's methods, such as {@code size()}, 924 * iterate across every key/value mapping in the underlying map and determine 925 * which satisfy the filter. When a live view is <i>not</i> needed, it may be 926 * faster to copy the filtered map and use the copy. 927 */ 928 public static <K, V> Map<K, V> filterValues( 929 Map<K, V> unfiltered, final Predicate<? super V> valuePredicate) { 930 checkNotNull(valuePredicate); 931 Predicate<Entry<K, V>> entryPredicate = 932 new Predicate<Entry<K, V>>() { 933 public boolean apply(Entry<K, V> input) { 934 return valuePredicate.apply(input.getValue()); 935 } 936 }; 937 return filterEntries(unfiltered, entryPredicate); 938 } 939 940 /** 941 * Returns a map containing the mappings in {@code unfiltered} that satisfy a 942 * predicate. The returned map is a live view of {@code unfiltered}; changes 943 * to one affect the other. 944 * 945 * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code 946 * values()} views have iterators that don't support {@code remove()}, but all 947 * other methods are supported by the map and its views. The map's {@code 948 * put()} and {@code putAll()} methods throw an {@link 949 * IllegalArgumentException} if a key/value pair that doesn't satisfy the 950 * predicate is provided. Similarly, the map's entries have a {@link 951 * Entry#setValue} method that throws an {@link IllegalArgumentException} when 952 * the existing key and the provided value don't satisfy the predicate. 953 * 954 * <p>When methods such as {@code removeAll()} and {@code clear()} are called 955 * on the filtered map or its views, only mappings that satisfy the filter 956 * will be removed from the underlying map. 957 * 958 * <p>The returned map isn't threadsafe or serializable, even if {@code 959 * unfiltered} is. 960 * 961 * <p>Many of the filtered map's methods, such as {@code size()}, 962 * iterate across every key/value mapping in the underlying map and determine 963 * which satisfy the filter. When a live view is <i>not</i> needed, it may be 964 * faster to copy the filtered map and use the copy. 965 */ 966 public static <K, V> Map<K, V> filterEntries( 967 Map<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { 968 checkNotNull(entryPredicate); 969 return (unfiltered instanceof AbstractFilteredMap) 970 ? filterFiltered((AbstractFilteredMap<K, V>) unfiltered, entryPredicate) 971 : new FilteredEntryMap<K, V>(checkNotNull(unfiltered), entryPredicate); 972 } 973 974 /** 975 * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when 976 * filtering a filtered map. 977 */ 978 private static <K, V> Map<K, V> filterFiltered(AbstractFilteredMap<K, V> map, 979 Predicate<? super Entry<K, V>> entryPredicate) { 980 Predicate<Entry<K, V>> predicate = 981 Predicates.and(map.predicate, entryPredicate); 982 return new FilteredEntryMap<K, V>(map.unfiltered, predicate); 983 } 984 985 private abstract static class AbstractFilteredMap<K, V> 986 extends AbstractMap<K, V> { 987 final Map<K, V> unfiltered; 988 final Predicate<? super Entry<K, V>> predicate; 989 990 AbstractFilteredMap( 991 Map<K, V> unfiltered, Predicate<? super Entry<K, V>> predicate) { 992 this.unfiltered = unfiltered; 993 this.predicate = predicate; 994 } 995 996 boolean apply(Object key, V value) { 997 // This method is called only when the key is in the map, implying that 998 // key is a K. 999 @SuppressWarnings("unchecked") 1000 K k = (K) key; 1001 return predicate.apply(Maps.immutableEntry(k, value)); 1002 } 1003 1004 @Override public V put(K key, V value) { 1005 checkArgument(apply(key, value)); 1006 return unfiltered.put(key, value); 1007 } 1008 1009 @Override public void putAll(Map<? extends K, ? extends V> map) { 1010 for (Entry<? extends K, ? extends V> entry : map.entrySet()) { 1011 checkArgument(apply(entry.getKey(), entry.getValue())); 1012 } 1013 unfiltered.putAll(map); 1014 } 1015 1016 @Override public boolean containsKey(Object key) { 1017 return unfiltered.containsKey(key) && apply(key, unfiltered.get(key)); 1018 } 1019 1020 @Override public V get(Object key) { 1021 V value = unfiltered.get(key); 1022 return ((value != null) && apply(key, value)) ? value : null; 1023 } 1024 1025 @Override public boolean isEmpty() { 1026 return entrySet().isEmpty(); 1027 } 1028 1029 @Override public V remove(Object key) { 1030 return containsKey(key) ? unfiltered.remove(key) : null; 1031 } 1032 1033 Collection<V> values; 1034 1035 @Override public Collection<V> values() { 1036 Collection<V> result = values; 1037 return (result == null) ? values = new Values() : result; 1038 } 1039 1040 class Values extends AbstractCollection<V> { 1041 @Override public Iterator<V> iterator() { 1042 final Iterator<Entry<K, V>> entryIterator = entrySet().iterator(); 1043 return new UnmodifiableIterator<V>() { 1044 public boolean hasNext() { 1045 return entryIterator.hasNext(); 1046 } 1047 1048 public V next() { 1049 return entryIterator.next().getValue(); 1050 } 1051 }; 1052 } 1053 1054 @Override public int size() { 1055 return entrySet().size(); 1056 } 1057 1058 @Override public void clear() { 1059 entrySet().clear(); 1060 } 1061 1062 @Override public boolean isEmpty() { 1063 return entrySet().isEmpty(); 1064 } 1065 1066 @Override public boolean remove(Object o) { 1067 Iterator<Entry<K, V>> iterator = unfiltered.entrySet().iterator(); 1068 while (iterator.hasNext()) { 1069 Entry<K, V> entry = iterator.next(); 1070 if (Objects.equal(o, entry.getValue()) && predicate.apply(entry)) { 1071 iterator.remove(); 1072 return true; 1073 } 1074 } 1075 return false; 1076 } 1077 1078 @Override public boolean removeAll(Collection<?> collection) { 1079 checkNotNull(collection); 1080 boolean changed = false; 1081 Iterator<Entry<K, V>> iterator = unfiltered.entrySet().iterator(); 1082 while (iterator.hasNext()) { 1083 Entry<K, V> entry = iterator.next(); 1084 if (collection.contains(entry.getValue()) && predicate.apply(entry)) { 1085 iterator.remove(); 1086 changed = true; 1087 } 1088 } 1089 return changed; 1090 } 1091 1092 @Override public boolean retainAll(Collection<?> collection) { 1093 checkNotNull(collection); 1094 boolean changed = false; 1095 Iterator<Entry<K, V>> iterator = unfiltered.entrySet().iterator(); 1096 while (iterator.hasNext()) { 1097 Entry<K, V> entry = iterator.next(); 1098 if (!collection.contains(entry.getValue()) 1099 && predicate.apply(entry)) { 1100 iterator.remove(); 1101 changed = true; 1102 } 1103 } 1104 return changed; 1105 } 1106 1107 @Override public Object[] toArray() { 1108 // creating an ArrayList so filtering happens once 1109 return Lists.newArrayList(iterator()).toArray(); 1110 } 1111 1112 @Override public <T> T[] toArray(T[] array) { 1113 return Lists.newArrayList(iterator()).toArray(array); 1114 } 1115 } 1116 } 1117 1118 private static class FilteredKeyMap<K, V> extends AbstractFilteredMap<K, V> { 1119 Predicate<? super K> keyPredicate; 1120 1121 FilteredKeyMap(Map<K, V> unfiltered, Predicate<? super K> keyPredicate, 1122 Predicate<Entry<K, V>> entryPredicate) { 1123 super(unfiltered, entryPredicate); 1124 this.keyPredicate = keyPredicate; 1125 } 1126 1127 Set<Entry<K, V>> entrySet; 1128 1129 @Override public Set<Entry<K, V>> entrySet() { 1130 Set<Entry<K, V>> result = entrySet; 1131 return (result == null) 1132 ? entrySet = Sets.filter(unfiltered.entrySet(), predicate) 1133 : result; 1134 } 1135 1136 Set<K> keySet; 1137 1138 @Override public Set<K> keySet() { 1139 Set<K> result = keySet; 1140 return (result == null) 1141 ? keySet = Sets.filter(unfiltered.keySet(), keyPredicate) 1142 : result; 1143 } 1144 1145 // The cast is called only when the key is in the unfiltered map, implying 1146 // that key is a K. 1147 @Override 1148 @SuppressWarnings("unchecked") 1149 public boolean containsKey(Object key) { 1150 return unfiltered.containsKey(key) && keyPredicate.apply((K) key); 1151 } 1152 } 1153 1154 private static class FilteredEntryMap<K, V> 1155 extends AbstractFilteredMap<K, V> { 1156 /** 1157 * Entries in this set satisfy the predicate, but they don't validate the 1158 * input to {@code Entry.setValue()}. 1159 */ 1160 final Set<Entry<K, V>> filteredEntrySet; 1161 1162 FilteredEntryMap( 1163 Map<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { 1164 super(unfiltered, entryPredicate); 1165 filteredEntrySet = Sets.filter(unfiltered.entrySet(), predicate); 1166 } 1167 1168 Set<Entry<K, V>> entrySet; 1169 1170 @Override public Set<Entry<K, V>> entrySet() { 1171 Set<Entry<K, V>> result = entrySet; 1172 return (result == null) ? entrySet = new EntrySet() : result; 1173 } 1174 1175 private class EntrySet extends ForwardingSet<Entry<K, V>> { 1176 @Override protected Set<Entry<K, V>> delegate() { 1177 return filteredEntrySet; 1178 } 1179 1180 @Override public Iterator<Entry<K, V>> iterator() { 1181 final Iterator<Entry<K, V>> iterator = filteredEntrySet.iterator(); 1182 return new UnmodifiableIterator<Entry<K, V>>() { 1183 public boolean hasNext() { 1184 return iterator.hasNext(); 1185 } 1186 1187 public Entry<K, V> next() { 1188 final Entry<K, V> entry = iterator.next(); 1189 return new ForwardingMapEntry<K, V>() { 1190 @Override protected Entry<K, V> delegate() { 1191 return entry; 1192 } 1193 1194 @Override public V setValue(V value) { 1195 checkArgument(apply(entry.getKey(), value)); 1196 return super.setValue(value); 1197 } 1198 }; 1199 } 1200 }; 1201 } 1202 } 1203 1204 Set<K> keySet; 1205 1206 @Override public Set<K> keySet() { 1207 Set<K> result = keySet; 1208 return (result == null) ? keySet = new KeySet() : result; 1209 } 1210 1211 private class KeySet extends AbstractSet<K> { 1212 @Override public Iterator<K> iterator() { 1213 final Iterator<Entry<K, V>> iterator = filteredEntrySet.iterator(); 1214 return new UnmodifiableIterator<K>() { 1215 public boolean hasNext() { 1216 return iterator.hasNext(); 1217 } 1218 1219 public K next() { 1220 return iterator.next().getKey(); 1221 } 1222 }; 1223 } 1224 1225 @Override public int size() { 1226 return filteredEntrySet.size(); 1227 } 1228 1229 @Override public void clear() { 1230 filteredEntrySet.clear(); 1231 } 1232 1233 @Override public boolean contains(Object o) { 1234 return containsKey(o); 1235 } 1236 1237 @Override public boolean remove(Object o) { 1238 if (containsKey(o)) { 1239 unfiltered.remove(o); 1240 return true; 1241 } 1242 return false; 1243 } 1244 1245 @Override public boolean removeAll(Collection<?> collection) { 1246 checkNotNull(collection); // for GWT 1247 boolean changed = false; 1248 for (Object obj : collection) { 1249 changed |= remove(obj); 1250 } 1251 return changed; 1252 } 1253 1254 @Override public boolean retainAll(Collection<?> collection) { 1255 checkNotNull(collection); // for GWT 1256 boolean changed = false; 1257 Iterator<Entry<K, V>> iterator = unfiltered.entrySet().iterator(); 1258 while (iterator.hasNext()) { 1259 Entry<K, V> entry = iterator.next(); 1260 if (!collection.contains(entry.getKey()) && predicate.apply(entry)) { 1261 iterator.remove(); 1262 changed = true; 1263 } 1264 } 1265 return changed; 1266 } 1267 1268 @Override public Object[] toArray() { 1269 // creating an ArrayList so filtering happens once 1270 return Lists.newArrayList(iterator()).toArray(); 1271 } 1272 1273 @Override public <T> T[] toArray(T[] array) { 1274 return Lists.newArrayList(iterator()).toArray(array); 1275 } 1276 } 1277 } 1278 1279 /** 1280 * {@code AbstractMap} extension that implements {@link #isEmpty()} as {@code 1281 * entrySet().isEmpty()} instead of {@code size() == 0} to speed up 1282 * implementations where {@code size()} is O(n), and it delegates the {@code 1283 * isEmpty()} methods of its key set and value collection to this 1284 * implementation. 1285 */ 1286 @GwtCompatible 1287 abstract static class ImprovedAbstractMap<K, V> extends AbstractMap<K, V> { 1288 /** 1289 * Creates the entry set to be returned by {@link #entrySet()}. This method 1290 * is invoked at most once on a given map, at the time when {@code entrySet} 1291 * is first called. 1292 */ 1293 protected abstract Set<Entry<K, V>> createEntrySet(); 1294 1295 private Set<Entry<K, V>> entrySet; 1296 1297 @Override public Set<Entry<K, V>> entrySet() { 1298 Set<Entry<K, V>> result = entrySet; 1299 if (result == null) { 1300 entrySet = result = createEntrySet(); 1301 } 1302 return result; 1303 } 1304 1305 private Set<K> keySet; 1306 1307 @Override public Set<K> keySet() { 1308 Set<K> result = keySet; 1309 if (result == null) { 1310 final Set<K> delegate = super.keySet(); 1311 keySet = result = 1312 new ForwardingSet<K>() { 1313 @Override protected Set<K> delegate() { 1314 return delegate; 1315 } 1316 1317 @Override public boolean isEmpty() { 1318 return ImprovedAbstractMap.this.isEmpty(); 1319 } 1320 }; 1321 } 1322 return result; 1323 } 1324 1325 private Collection<V> values; 1326 1327 @Override public Collection<V> values() { 1328 Collection<V> result = values; 1329 if (result == null) { 1330 final Collection<V> delegate = super.values(); 1331 values = result = 1332 new ForwardingCollection<V>() { 1333 @Override protected Collection<V> delegate() { 1334 return delegate; 1335 } 1336 1337 @Override public boolean isEmpty() { 1338 return ImprovedAbstractMap.this.isEmpty(); 1339 } 1340 }; 1341 } 1342 return result; 1343 } 1344 1345 /** 1346 * Returns {@code true} if this map contains no key-value mappings. 1347 * 1348 * <p>The implementation returns {@code entrySet().isEmpty()}. 1349 * 1350 * @return {@code true} if this map contains no key-value mappings 1351 */ 1352 @Override public boolean isEmpty() { 1353 return entrySet().isEmpty(); 1354 } 1355 } 1356 1357 static final MapJoiner standardJoiner = 1358 Collections2.standardJoiner.withKeyValueSeparator("="); 1359 1360 /** 1361 * Delegates to {@link Map#get}. Returns {@code null} on {@code 1362 * ClassCastException}. 1363 */ 1364 static <V> V safeGet(Map<?, V> map, Object key) { 1365 try { 1366 return map.get(key); 1367 } catch (ClassCastException e) { 1368 return null; 1369 } 1370 } 1371 1372 /** 1373 * Delegates to {@link Map#containsKey}. Returns {@code false} on {@code 1374 * ClassCastException} 1375 */ 1376 static boolean safeContainsKey(Map<?, ?> map, Object key) { 1377 try { 1378 return map.containsKey(key); 1379 } catch (ClassCastException e) { 1380 return false; 1381 } 1382 } 1383 }