1 /* 2 * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util; 27 import java.util.Map.Entry; 28 29 /** 30 * This class provides a skeletal implementation of the {@code Map} 31 * interface, to minimize the effort required to implement this interface. 32 * 33 * <p>To implement an unmodifiable map, the programmer needs only to extend this 34 * class and provide an implementation for the {@code entrySet} method, which 35 * returns a set-view of the map's mappings. Typically, the returned set 36 * will, in turn, be implemented atop {@code AbstractSet}. This set should 37 * not support the {@code add} or {@code remove} methods, and its iterator 38 * should not support the {@code remove} method. 39 * 40 * <p>To implement a modifiable map, the programmer must additionally override 41 * this class's {@code put} method (which otherwise throws an 42 * {@code UnsupportedOperationException}), and the iterator returned by 43 * {@code entrySet().iterator()} must additionally implement its 44 * {@code remove} method. 45 * 46 * <p>The programmer should generally provide a void (no argument) and map 47 * constructor, as per the recommendation in the {@code Map} interface 48 * specification. 49 * 50 * <p>The documentation for each non-abstract method in this class describes its 51 * implementation in detail. Each of these methods may be overridden if the 52 * map being implemented admits a more efficient implementation. 53 * 54 * <p>This class is a member of the 55 * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework"> 56 * Java Collections Framework</a>. 57 * 58 * @param <K> the type of keys maintained by this map 59 * @param <V> the type of mapped values 60 * 61 * @author Josh Bloch 62 * @author Neal Gafter 63 * @see Map 64 * @see Collection 65 * @since 1.2 66 */ 67 68 public abstract class AbstractMap<K,V> implements Map<K,V> { 69 /** 70 * Sole constructor. (For invocation by subclass constructors, typically 71 * implicit.) 72 */ AbstractMap()73 protected AbstractMap() { 74 } 75 76 // Query Operations 77 78 /** 79 * {@inheritDoc} 80 * 81 * @implSpec 82 * This implementation returns {@code entrySet().size()}. 83 */ size()84 public int size() { 85 return entrySet().size(); 86 } 87 88 /** 89 * {@inheritDoc} 90 * 91 * @implSpec 92 * This implementation returns {@code size() == 0}. 93 */ isEmpty()94 public boolean isEmpty() { 95 return size() == 0; 96 } 97 98 /** 99 * {@inheritDoc} 100 * 101 * @implSpec 102 * This implementation iterates over {@code entrySet()} searching 103 * for an entry with the specified value. If such an entry is found, 104 * {@code true} is returned. If the iteration terminates without 105 * finding such an entry, {@code false} is returned. Note that this 106 * implementation requires linear time in the size of the map. 107 * 108 * @throws ClassCastException {@inheritDoc} 109 * @throws NullPointerException {@inheritDoc} 110 */ containsValue(Object value)111 public boolean containsValue(Object value) { 112 Iterator<Entry<K,V>> i = entrySet().iterator(); 113 if (value==null) { 114 while (i.hasNext()) { 115 Entry<K,V> e = i.next(); 116 if (e.getValue()==null) 117 return true; 118 } 119 } else { 120 while (i.hasNext()) { 121 Entry<K,V> e = i.next(); 122 if (value.equals(e.getValue())) 123 return true; 124 } 125 } 126 return false; 127 } 128 129 /** 130 * {@inheritDoc} 131 * 132 * @implSpec 133 * This implementation iterates over {@code entrySet()} searching 134 * for an entry with the specified key. If such an entry is found, 135 * {@code true} is returned. If the iteration terminates without 136 * finding such an entry, {@code false} is returned. Note that this 137 * implementation requires linear time in the size of the map; many 138 * implementations will override this method. 139 * 140 * @throws ClassCastException {@inheritDoc} 141 * @throws NullPointerException {@inheritDoc} 142 */ containsKey(Object key)143 public boolean containsKey(Object key) { 144 Iterator<Map.Entry<K,V>> i = entrySet().iterator(); 145 if (key==null) { 146 while (i.hasNext()) { 147 Entry<K,V> e = i.next(); 148 if (e.getKey()==null) 149 return true; 150 } 151 } else { 152 while (i.hasNext()) { 153 Entry<K,V> e = i.next(); 154 if (key.equals(e.getKey())) 155 return true; 156 } 157 } 158 return false; 159 } 160 161 /** 162 * {@inheritDoc} 163 * 164 * @implSpec 165 * This implementation iterates over {@code entrySet()} searching 166 * for an entry with the specified key. If such an entry is found, 167 * the entry's value is returned. If the iteration terminates without 168 * finding such an entry, {@code null} is returned. Note that this 169 * implementation requires linear time in the size of the map; many 170 * implementations will override this method. 171 * 172 * @throws ClassCastException {@inheritDoc} 173 * @throws NullPointerException {@inheritDoc} 174 */ get(Object key)175 public V get(Object key) { 176 Iterator<Entry<K,V>> i = entrySet().iterator(); 177 if (key==null) { 178 while (i.hasNext()) { 179 Entry<K,V> e = i.next(); 180 if (e.getKey()==null) 181 return e.getValue(); 182 } 183 } else { 184 while (i.hasNext()) { 185 Entry<K,V> e = i.next(); 186 if (key.equals(e.getKey())) 187 return e.getValue(); 188 } 189 } 190 return null; 191 } 192 193 194 // Modification Operations 195 196 /** 197 * {@inheritDoc} 198 * 199 * @implSpec 200 * This implementation always throws an 201 * {@code UnsupportedOperationException}. 202 * 203 * @throws UnsupportedOperationException {@inheritDoc} 204 * @throws ClassCastException {@inheritDoc} 205 * @throws NullPointerException {@inheritDoc} 206 * @throws IllegalArgumentException {@inheritDoc} 207 */ put(K key, V value)208 public V put(K key, V value) { 209 throw new UnsupportedOperationException(); 210 } 211 212 /** 213 * {@inheritDoc} 214 * 215 * @implSpec 216 * This implementation iterates over {@code entrySet()} searching for an 217 * entry with the specified key. If such an entry is found, its value is 218 * obtained with its {@code getValue} operation, the entry is removed 219 * from the collection (and the backing map) with the iterator's 220 * {@code remove} operation, and the saved value is returned. If the 221 * iteration terminates without finding such an entry, {@code null} is 222 * returned. Note that this implementation requires linear time in the 223 * size of the map; many implementations will override this method. 224 * 225 * <p>Note that this implementation throws an 226 * {@code UnsupportedOperationException} if the {@code entrySet} 227 * iterator does not support the {@code remove} method and this map 228 * contains a mapping for the specified key. 229 * 230 * @throws UnsupportedOperationException {@inheritDoc} 231 * @throws ClassCastException {@inheritDoc} 232 * @throws NullPointerException {@inheritDoc} 233 */ remove(Object key)234 public V remove(Object key) { 235 Iterator<Entry<K,V>> i = entrySet().iterator(); 236 Entry<K,V> correctEntry = null; 237 if (key==null) { 238 while (correctEntry==null && i.hasNext()) { 239 Entry<K,V> e = i.next(); 240 if (e.getKey()==null) 241 correctEntry = e; 242 } 243 } else { 244 while (correctEntry==null && i.hasNext()) { 245 Entry<K,V> e = i.next(); 246 if (key.equals(e.getKey())) 247 correctEntry = e; 248 } 249 } 250 251 V oldValue = null; 252 if (correctEntry !=null) { 253 oldValue = correctEntry.getValue(); 254 i.remove(); 255 } 256 return oldValue; 257 } 258 259 260 // Bulk Operations 261 262 /** 263 * {@inheritDoc} 264 * 265 * @implSpec 266 * This implementation iterates over the specified map's 267 * {@code entrySet()} collection, and calls this map's {@code put} 268 * operation once for each entry returned by the iteration. 269 * 270 * <p>Note that this implementation throws an 271 * {@code UnsupportedOperationException} if this map does not support 272 * the {@code put} operation and the specified map is nonempty. 273 * 274 * @throws UnsupportedOperationException {@inheritDoc} 275 * @throws ClassCastException {@inheritDoc} 276 * @throws NullPointerException {@inheritDoc} 277 * @throws IllegalArgumentException {@inheritDoc} 278 */ putAll(Map<? extends K, ? extends V> m)279 public void putAll(Map<? extends K, ? extends V> m) { 280 for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) 281 put(e.getKey(), e.getValue()); 282 } 283 284 /** 285 * {@inheritDoc} 286 * 287 * @implSpec 288 * This implementation calls {@code entrySet().clear()}. 289 * 290 * <p>Note that this implementation throws an 291 * {@code UnsupportedOperationException} if the {@code entrySet} 292 * does not support the {@code clear} operation. 293 * 294 * @throws UnsupportedOperationException {@inheritDoc} 295 */ clear()296 public void clear() { 297 entrySet().clear(); 298 } 299 300 301 // Views 302 303 /** 304 * Each of these fields are initialized to contain an instance of the 305 * appropriate view the first time this view is requested. The views are 306 * stateless, so there's no reason to create more than one of each. 307 * 308 * <p>Since there is no synchronization performed while accessing these fields, 309 * it is expected that java.util.Map view classes using these fields have 310 * no non-final fields (or any fields at all except for outer-this). Adhering 311 * to this rule would make the races on these fields benign. 312 * 313 * <p>It is also imperative that implementations read the field only once, 314 * as in: 315 * 316 * <pre> {@code 317 * public Set<K> keySet() { 318 * Set<K> ks = keySet; // single racy read 319 * if (ks == null) { 320 * ks = new KeySet(); 321 * keySet = ks; 322 * } 323 * return ks; 324 * } 325 *}</pre> 326 */ 327 transient Set<K> keySet; 328 transient Collection<V> values; 329 330 /** 331 * {@inheritDoc} 332 * 333 * @implSpec 334 * This implementation returns a set that subclasses {@link AbstractSet}. 335 * The subclass's iterator method returns a "wrapper object" over this 336 * map's {@code entrySet()} iterator. The {@code size} method 337 * delegates to this map's {@code size} method and the 338 * {@code contains} method delegates to this map's 339 * {@code containsKey} method. 340 * 341 * <p>The set is created the first time this method is called, 342 * and returned in response to all subsequent calls. No synchronization 343 * is performed, so there is a slight chance that multiple calls to this 344 * method will not all return the same set. 345 */ keySet()346 public Set<K> keySet() { 347 Set<K> ks = keySet; 348 if (ks == null) { 349 ks = new AbstractSet<K>() { 350 public Iterator<K> iterator() { 351 return new Iterator<K>() { 352 private Iterator<Entry<K,V>> i = entrySet().iterator(); 353 354 public boolean hasNext() { 355 return i.hasNext(); 356 } 357 358 public K next() { 359 return i.next().getKey(); 360 } 361 362 public void remove() { 363 i.remove(); 364 } 365 }; 366 } 367 368 public int size() { 369 return AbstractMap.this.size(); 370 } 371 372 public boolean isEmpty() { 373 return AbstractMap.this.isEmpty(); 374 } 375 376 public void clear() { 377 AbstractMap.this.clear(); 378 } 379 380 public boolean contains(Object k) { 381 return AbstractMap.this.containsKey(k); 382 } 383 }; 384 keySet = ks; 385 } 386 return ks; 387 } 388 389 /** 390 * {@inheritDoc} 391 * 392 * @implSpec 393 * This implementation returns a collection that subclasses {@link 394 * AbstractCollection}. The subclass's iterator method returns a 395 * "wrapper object" over this map's {@code entrySet()} iterator. 396 * The {@code size} method delegates to this map's {@code size} 397 * method and the {@code contains} method delegates to this map's 398 * {@code containsValue} method. 399 * 400 * <p>The collection is created the first time this method is called, and 401 * returned in response to all subsequent calls. No synchronization is 402 * performed, so there is a slight chance that multiple calls to this 403 * method will not all return the same collection. 404 */ values()405 public Collection<V> values() { 406 Collection<V> vals = values; 407 if (vals == null) { 408 vals = new AbstractCollection<V>() { 409 public Iterator<V> iterator() { 410 return new Iterator<V>() { 411 private Iterator<Entry<K,V>> i = entrySet().iterator(); 412 413 public boolean hasNext() { 414 return i.hasNext(); 415 } 416 417 public V next() { 418 return i.next().getValue(); 419 } 420 421 public void remove() { 422 i.remove(); 423 } 424 }; 425 } 426 427 public int size() { 428 return AbstractMap.this.size(); 429 } 430 431 public boolean isEmpty() { 432 return AbstractMap.this.isEmpty(); 433 } 434 435 public void clear() { 436 AbstractMap.this.clear(); 437 } 438 439 public boolean contains(Object v) { 440 return AbstractMap.this.containsValue(v); 441 } 442 }; 443 values = vals; 444 } 445 return vals; 446 } 447 entrySet()448 public abstract Set<Entry<K,V>> entrySet(); 449 450 451 // Comparison and hashing 452 453 /** 454 * Compares the specified object with this map for equality. Returns 455 * {@code true} if the given object is also a map and the two maps 456 * represent the same mappings. More formally, two maps {@code m1} and 457 * {@code m2} represent the same mappings if 458 * {@code m1.entrySet().equals(m2.entrySet())}. This ensures that the 459 * {@code equals} method works properly across different implementations 460 * of the {@code Map} interface. 461 * 462 * @implSpec 463 * This implementation first checks if the specified object is this map; 464 * if so it returns {@code true}. Then, it checks if the specified 465 * object is a map whose size is identical to the size of this map; if 466 * not, it returns {@code false}. If so, it iterates over this map's 467 * {@code entrySet} collection, and checks that the specified map 468 * contains each mapping that this map contains. If the specified map 469 * fails to contain such a mapping, {@code false} is returned. If the 470 * iteration completes, {@code true} is returned. 471 * 472 * @param o object to be compared for equality with this map 473 * @return {@code true} if the specified object is equal to this map 474 */ equals(Object o)475 public boolean equals(Object o) { 476 if (o == this) 477 return true; 478 479 if (!(o instanceof Map<?, ?> m)) 480 return false; 481 if (m.size() != size()) 482 return false; 483 484 try { 485 for (Entry<K, V> e : entrySet()) { 486 K key = e.getKey(); 487 V value = e.getValue(); 488 if (value == null) { 489 if (!(m.get(key) == null && m.containsKey(key))) 490 return false; 491 } else { 492 if (!value.equals(m.get(key))) 493 return false; 494 } 495 } 496 } catch (ClassCastException unused) { 497 return false; 498 } catch (NullPointerException unused) { 499 return false; 500 } 501 502 return true; 503 } 504 505 /** 506 * Returns the hash code value for this map. The hash code of a map is 507 * defined to be the sum of the hash codes of each entry in the map's 508 * {@code entrySet()} view. This ensures that {@code m1.equals(m2)} 509 * implies that {@code m1.hashCode()==m2.hashCode()} for any two maps 510 * {@code m1} and {@code m2}, as required by the general contract of 511 * {@link Object#hashCode}. 512 * 513 * @implSpec 514 * This implementation iterates over {@code entrySet()}, calling 515 * {@link Map.Entry#hashCode hashCode()} on each element (entry) in the 516 * set, and adding up the results. 517 * 518 * @return the hash code value for this map 519 * @see Map.Entry#hashCode() 520 * @see Object#equals(Object) 521 * @see Set#equals(Object) 522 */ hashCode()523 public int hashCode() { 524 int h = 0; 525 for (Entry<K, V> entry : entrySet()) 526 h += entry.hashCode(); 527 return h; 528 } 529 530 /** 531 * Returns a string representation of this map. The string representation 532 * consists of a list of key-value mappings in the order returned by the 533 * map's {@code entrySet} view's iterator, enclosed in braces 534 * ({@code "{}"}). Adjacent mappings are separated by the characters 535 * {@code ", "} (comma and space). Each key-value mapping is rendered as 536 * the key followed by an equals sign ({@code "="}) followed by the 537 * associated value. Keys and values are converted to strings as by 538 * {@link String#valueOf(Object)}. 539 * 540 * @return a string representation of this map 541 */ toString()542 public String toString() { 543 Iterator<Entry<K,V>> i = entrySet().iterator(); 544 if (! i.hasNext()) 545 return "{}"; 546 547 StringBuilder sb = new StringBuilder(); 548 sb.append('{'); 549 for (;;) { 550 Entry<K,V> e = i.next(); 551 K key = e.getKey(); 552 V value = e.getValue(); 553 sb.append(key == this ? "(this Map)" : key); 554 sb.append('='); 555 sb.append(value == this ? "(this Map)" : value); 556 if (! i.hasNext()) 557 return sb.append('}').toString(); 558 sb.append(',').append(' '); 559 } 560 } 561 562 /** 563 * Returns a shallow copy of this {@code AbstractMap} instance: the keys 564 * and values themselves are not cloned. 565 * 566 * @return a shallow copy of this map 567 */ clone()568 protected Object clone() throws CloneNotSupportedException { 569 AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone(); 570 result.keySet = null; 571 result.values = null; 572 return result; 573 } 574 575 /** 576 * Utility method for SimpleEntry and SimpleImmutableEntry. 577 * Test for equality, checking for nulls. 578 * 579 * NB: Do not replace with Object.equals until JDK-8015417 is resolved. 580 */ eq(Object o1, Object o2)581 private static boolean eq(Object o1, Object o2) { 582 return o1 == null ? o2 == null : o1.equals(o2); 583 } 584 585 // Implementation Note: SimpleEntry and SimpleImmutableEntry 586 // are distinct unrelated classes, even though they share 587 // some code. Since you can't add or subtract final-ness 588 // of a field in a subclass, they can't share representations, 589 // and the amount of duplicated code is too small to warrant 590 // exposing a common abstract class. 591 592 593 /** 594 * An Entry maintaining a key and a value. The value may be 595 * changed using the {@code setValue} method. Instances of 596 * this class are not associated with any map's entry-set view. 597 * 598 * @apiNote 599 * This class facilitates the process of building custom map 600 * implementations. For example, it may be convenient to return 601 * arrays of {@code SimpleEntry} instances in method 602 * {@code Map.entrySet().toArray}. 603 * 604 * @since 1.6 605 */ 606 public static class SimpleEntry<K,V> 607 implements Entry<K,V>, java.io.Serializable 608 { 609 @java.io.Serial 610 private static final long serialVersionUID = -8499721149061103585L; 611 612 @SuppressWarnings("serial") // Conditionally serializable 613 private final K key; 614 @SuppressWarnings("serial") // Conditionally serializable 615 private V value; 616 617 /** 618 * Creates an entry representing a mapping from the specified 619 * key to the specified value. 620 * 621 * @param key the key represented by this entry 622 * @param value the value represented by this entry 623 */ SimpleEntry(K key, V value)624 public SimpleEntry(K key, V value) { 625 this.key = key; 626 this.value = value; 627 } 628 629 /** 630 * Creates an entry representing the same mapping as the 631 * specified entry. 632 * 633 * @param entry the entry to copy 634 */ SimpleEntry(Entry<? extends K, ? extends V> entry)635 public SimpleEntry(Entry<? extends K, ? extends V> entry) { 636 this.key = entry.getKey(); 637 this.value = entry.getValue(); 638 } 639 640 /** 641 * Returns the key corresponding to this entry. 642 * 643 * @return the key corresponding to this entry 644 */ getKey()645 public K getKey() { 646 return key; 647 } 648 649 /** 650 * Returns the value corresponding to this entry. 651 * 652 * @return the value corresponding to this entry 653 */ getValue()654 public V getValue() { 655 return value; 656 } 657 658 /** 659 * Replaces the value corresponding to this entry with the specified 660 * value. 661 * 662 * @param value new value to be stored in this entry 663 * @return the old value corresponding to the entry 664 */ setValue(V value)665 public V setValue(V value) { 666 V oldValue = this.value; 667 this.value = value; 668 return oldValue; 669 } 670 671 /** 672 * Compares the specified object with this entry for equality. 673 * Returns {@code true} if the given object is also a map entry and 674 * the two entries represent the same mapping. More formally, two 675 * entries {@code e1} and {@code e2} represent the same mapping 676 * if<pre> 677 * (e1.getKey()==null ? 678 * e2.getKey()==null : 679 * e1.getKey().equals(e2.getKey())) 680 * && 681 * (e1.getValue()==null ? 682 * e2.getValue()==null : 683 * e1.getValue().equals(e2.getValue()))</pre> 684 * This ensures that the {@code equals} method works properly across 685 * different implementations of the {@code Map.Entry} interface. 686 * 687 * @param o object to be compared for equality with this map entry 688 * @return {@code true} if the specified object is equal to this map 689 * entry 690 * @see #hashCode 691 */ equals(Object o)692 public boolean equals(Object o) { 693 return o instanceof Map.Entry<?, ?> e 694 && eq(key, e.getKey()) 695 && eq(value, e.getValue()); 696 } 697 698 /** 699 * Returns the hash code value for this map entry. The hash code 700 * of a map entry {@code e} is defined to be: <pre> 701 * (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ 702 * (e.getValue()==null ? 0 : e.getValue().hashCode())</pre> 703 * This ensures that {@code e1.equals(e2)} implies that 704 * {@code e1.hashCode()==e2.hashCode()} for any two Entries 705 * {@code e1} and {@code e2}, as required by the general 706 * contract of {@link Object#hashCode}. 707 * 708 * @return the hash code value for this map entry 709 * @see #equals 710 */ hashCode()711 public int hashCode() { 712 return (key == null ? 0 : key.hashCode()) ^ 713 (value == null ? 0 : value.hashCode()); 714 } 715 716 /** 717 * Returns a String representation of this map entry. This 718 * implementation returns the string representation of this 719 * entry's key followed by the equals character ("{@code =}") 720 * followed by the string representation of this entry's value. 721 * 722 * @return a String representation of this map entry 723 */ toString()724 public String toString() { 725 return key + "=" + value; 726 } 727 728 } 729 730 /** 731 * An unmodifiable Entry maintaining a key and a value. This class 732 * does not support the {@code setValue} method. Instances of 733 * this class are not associated with any map's entry-set view. 734 * 735 * @apiNote 736 * Instances of this class are not necessarily immutable, as the key 737 * and value may be mutable. An instance of <i>this specific class</i> 738 * is unmodifiable, because the key and value references cannot be 739 * changed. A reference of this <i>type</i> may not be unmodifiable, 740 * as a subclass may be modifiable or may provide the appearance of modifiability. 741 * <p> 742 * This class may be convenient in methods that return thread-safe snapshots of 743 * key-value mappings. For alternatives, see the 744 * {@link Map#entry Map::entry} and {@link Map.Entry#copyOf Map.Entry::copyOf} 745 * methods. 746 * 747 * @since 1.6 748 */ 749 public static class SimpleImmutableEntry<K,V> 750 implements Entry<K,V>, java.io.Serializable 751 { 752 @java.io.Serial 753 private static final long serialVersionUID = 7138329143949025153L; 754 755 @SuppressWarnings("serial") // Not statically typed as Serializable 756 private final K key; 757 @SuppressWarnings("serial") // Not statically typed as Serializable 758 private final V value; 759 760 /** 761 * Creates an entry representing a mapping from the specified 762 * key to the specified value. 763 * 764 * @param key the key represented by this entry 765 * @param value the value represented by this entry 766 */ SimpleImmutableEntry(K key, V value)767 public SimpleImmutableEntry(K key, V value) { 768 this.key = key; 769 this.value = value; 770 } 771 772 /** 773 * Creates an entry representing the same mapping as the 774 * specified entry. 775 * 776 * @param entry the entry to copy 777 */ SimpleImmutableEntry(Entry<? extends K, ? extends V> entry)778 public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) { 779 this.key = entry.getKey(); 780 this.value = entry.getValue(); 781 } 782 783 /** 784 * Returns the key corresponding to this entry. 785 * 786 * @return the key corresponding to this entry 787 */ getKey()788 public K getKey() { 789 return key; 790 } 791 792 /** 793 * Returns the value corresponding to this entry. 794 * 795 * @return the value corresponding to this entry 796 */ getValue()797 public V getValue() { 798 return value; 799 } 800 801 /** 802 * Replaces the value corresponding to this entry with the specified 803 * value (optional operation). This implementation simply throws 804 * {@code UnsupportedOperationException}, as this class implements 805 * an unmodifiable map entry. 806 * 807 * @implSpec 808 * The implementation in this class always throws {@code UnsupportedOperationException}. 809 * 810 * @param value new value to be stored in this entry 811 * @return (Does not return) 812 * @throws UnsupportedOperationException always 813 */ setValue(V value)814 public V setValue(V value) { 815 throw new UnsupportedOperationException(); 816 } 817 818 /** 819 * Compares the specified object with this entry for equality. 820 * Returns {@code true} if the given object is also a map entry and 821 * the two entries represent the same mapping. More formally, two 822 * entries {@code e1} and {@code e2} represent the same mapping 823 * if<pre> 824 * (e1.getKey()==null ? 825 * e2.getKey()==null : 826 * e1.getKey().equals(e2.getKey())) 827 * && 828 * (e1.getValue()==null ? 829 * e2.getValue()==null : 830 * e1.getValue().equals(e2.getValue()))</pre> 831 * This ensures that the {@code equals} method works properly across 832 * different implementations of the {@code Map.Entry} interface. 833 * 834 * @param o object to be compared for equality with this map entry 835 * @return {@code true} if the specified object is equal to this map 836 * entry 837 * @see #hashCode 838 */ equals(Object o)839 public boolean equals(Object o) { 840 return o instanceof Map.Entry<?, ?> e 841 && eq(key, e.getKey()) 842 && eq(value, e.getValue()); 843 } 844 845 /** 846 * Returns the hash code value for this map entry. The hash code 847 * of a map entry {@code e} is defined to be: <pre> 848 * (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ 849 * (e.getValue()==null ? 0 : e.getValue().hashCode())</pre> 850 * This ensures that {@code e1.equals(e2)} implies that 851 * {@code e1.hashCode()==e2.hashCode()} for any two Entries 852 * {@code e1} and {@code e2}, as required by the general 853 * contract of {@link Object#hashCode}. 854 * 855 * @return the hash code value for this map entry 856 * @see #equals 857 */ hashCode()858 public int hashCode() { 859 return (key == null ? 0 : key.hashCode()) ^ 860 (value == null ? 0 : value.hashCode()); 861 } 862 863 /** 864 * Returns a String representation of this map entry. This 865 * implementation returns the string representation of this 866 * entry's key followed by the equals character ("{@code =}") 867 * followed by the string representation of this entry's value. 868 * 869 * @return a String representation of this map entry 870 */ toString()871 public String toString() { 872 return key + "=" + value; 873 } 874 875 } 876 877 } 878