1 /*
   2  * Copyright (c) 1997, 2018, 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      */
  73     protected AbstractMap() {
  74     }
  75 
  76     // Query Operations
  77 
  78     /**
  79      * {@inheritDoc}
  80      *
  81      * @implSpec
  82      * This implementation returns {@code entrySet().size()}.
  83      */
  84     public int size() {
  85         return entrySet().size();
  86     }
  87 
  88     /**
  89      * {@inheritDoc}
  90      *
  91      * @implSpec
  92      * This implementation returns {@code size() == 0}.
  93      */
  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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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 
 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      */
 475     public boolean equals(Object o) {
 476         if (o == this)
 477             return true;
 478 
 479         if (!(o instanceof Map))
 480             return false;
 481         Map<?,?> m = (Map<?,?>) o;
 482         if (m.size() != size())
 483             return false;
 484 
 485         try {
 486             for (Entry<K, V> e : entrySet()) {
 487                 K key = e.getKey();
 488                 V value = e.getValue();
 489                 if (value == null) {
 490                     if (!(m.get(key) == null && m.containsKey(key)))
 491                         return false;
 492                 } else {
 493                     if (!value.equals(m.get(key)))
 494                         return false;
 495                 }
 496             }
 497         } catch (ClassCastException unused) {
 498             return false;
 499         } catch (NullPointerException unused) {
 500             return false;
 501         }
 502 
 503         return true;
 504     }
 505 
 506     /**
 507      * Returns the hash code value for this map.  The hash code of a map is
 508      * defined to be the sum of the hash codes of each entry in the map's
 509      * {@code entrySet()} view.  This ensures that {@code m1.equals(m2)}
 510      * implies that {@code m1.hashCode()==m2.hashCode()} for any two maps
 511      * {@code m1} and {@code m2}, as required by the general contract of
 512      * {@link Object#hashCode}.
 513      *
 514      * @implSpec
 515      * This implementation iterates over {@code entrySet()}, calling
 516      * {@link Map.Entry#hashCode hashCode()} on each element (entry) in the
 517      * set, and adding up the results.
 518      *
 519      * @return the hash code value for this map
 520      * @see Map.Entry#hashCode()
 521      * @see Object#equals(Object)
 522      * @see Set#equals(Object)
 523      */
 524     public int hashCode() {
 525         int h = 0;
 526         for (Entry<K, V> entry : entrySet())
 527             h += entry.hashCode();
 528         return h;
 529     }
 530 
 531     /**
 532      * Returns a string representation of this map.  The string representation
 533      * consists of a list of key-value mappings in the order returned by the
 534      * map's {@code entrySet} view's iterator, enclosed in braces
 535      * ({@code "{}"}).  Adjacent mappings are separated by the characters
 536      * {@code ", "} (comma and space).  Each key-value mapping is rendered as
 537      * the key followed by an equals sign ({@code "="}) followed by the
 538      * associated value.  Keys and values are converted to strings as by
 539      * {@link String#valueOf(Object)}.
 540      *
 541      * @return a string representation of this map
 542      */
 543     public String toString() {
 544         Iterator<Entry<K,V>> i = entrySet().iterator();
 545         if (! i.hasNext())
 546             return "{}";
 547 
 548         StringBuilder sb = new StringBuilder();
 549         sb.append('{');
 550         for (;;) {
 551             Entry<K,V> e = i.next();
 552             K key = e.getKey();
 553             V value = e.getValue();
 554             sb.append(key   == this ? "(this Map)" : key);
 555             sb.append('=');
 556             sb.append(value == this ? "(this Map)" : value);
 557             if (! i.hasNext())
 558                 return sb.append('}').toString();
 559             sb.append(',').append(' ');
 560         }
 561     }
 562 
 563     /**
 564      * Returns a shallow copy of this {@code AbstractMap} instance: the keys
 565      * and values themselves are not cloned.
 566      *
 567      * @return a shallow copy of this map
 568      */
 569     protected Object clone() throws CloneNotSupportedException {
 570         AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
 571         result.keySet = null;
 572         result.values = null;
 573         return result;
 574     }
 575 
 576     /**
 577      * Utility method for SimpleEntry and SimpleImmutableEntry.
 578      * Test for equality, checking for nulls.
 579      *
 580      * NB: Do not replace with Object.equals until JDK-8015417 is resolved.
 581      */
 582     private static boolean eq(Object o1, Object o2) {
 583         return o1 == null ? o2 == null : o1.equals(o2);
 584     }
 585 
 586     // Implementation Note: SimpleEntry and SimpleImmutableEntry
 587     // are distinct unrelated classes, even though they share
 588     // some code. Since you can't add or subtract final-ness
 589     // of a field in a subclass, they can't share representations,
 590     // and the amount of duplicated code is too small to warrant
 591     // exposing a common abstract class.
 592 
 593 
 594     /**
 595      * An Entry maintaining a key and a value.  The value may be
 596      * changed using the {@code setValue} method.  This class
 597      * facilitates the process of building custom map
 598      * implementations. For example, it may be convenient to return
 599      * arrays of {@code SimpleEntry} instances in method
 600      * {@code Map.entrySet().toArray}.
 601      *
 602      * @since 1.6
 603      */
 604     public static class SimpleEntry<K,V>
 605         implements Entry<K,V>, java.io.Serializable
 606     {
 607         @java.io.Serial
 608         private static final long serialVersionUID = -8499721149061103585L;
 609 
 610         private final K key;
 611         private V value;
 612 
 613         /**
 614          * Creates an entry representing a mapping from the specified
 615          * key to the specified value.
 616          *
 617          * @param key the key represented by this entry
 618          * @param value the value represented by this entry
 619          */
 620         public SimpleEntry(K key, V value) {
 621             this.key   = key;
 622             this.value = value;
 623         }
 624 
 625         /**
 626          * Creates an entry representing the same mapping as the
 627          * specified entry.
 628          *
 629          * @param entry the entry to copy
 630          */
 631         public SimpleEntry(Entry<? extends K, ? extends V> entry) {
 632             this.key   = entry.getKey();
 633             this.value = entry.getValue();
 634         }
 635 
 636         /**
 637          * Returns the key corresponding to this entry.
 638          *
 639          * @return the key corresponding to this entry
 640          */
 641         public K getKey() {
 642             return key;
 643         }
 644 
 645         /**
 646          * Returns the value corresponding to this entry.
 647          *
 648          * @return the value corresponding to this entry
 649          */
 650         public V getValue() {
 651             return value;
 652         }
 653 
 654         /**
 655          * Replaces the value corresponding to this entry with the specified
 656          * value.
 657          *
 658          * @param value new value to be stored in this entry
 659          * @return the old value corresponding to the entry
 660          */
 661         public V setValue(V value) {
 662             V oldValue = this.value;
 663             this.value = value;
 664             return oldValue;
 665         }
 666 
 667         /**
 668          * Compares the specified object with this entry for equality.
 669          * Returns {@code true} if the given object is also a map entry and
 670          * the two entries represent the same mapping.  More formally, two
 671          * entries {@code e1} and {@code e2} represent the same mapping
 672          * if<pre>
 673          *   (e1.getKey()==null ?
 674          *    e2.getKey()==null :
 675          *    e1.getKey().equals(e2.getKey()))
 676          *   &amp;&amp;
 677          *   (e1.getValue()==null ?
 678          *    e2.getValue()==null :
 679          *    e1.getValue().equals(e2.getValue()))</pre>
 680          * This ensures that the {@code equals} method works properly across
 681          * different implementations of the {@code Map.Entry} interface.
 682          *
 683          * @param o object to be compared for equality with this map entry
 684          * @return {@code true} if the specified object is equal to this map
 685          *         entry
 686          * @see    #hashCode
 687          */
 688         public boolean equals(Object o) {
 689             if (!(o instanceof Map.Entry))
 690                 return false;
 691             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
 692             return eq(key, e.getKey()) && eq(value, e.getValue());
 693         }
 694 
 695         /**
 696          * Returns the hash code value for this map entry.  The hash code
 697          * of a map entry {@code e} is defined to be: <pre>
 698          *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
 699          *   (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
 700          * This ensures that {@code e1.equals(e2)} implies that
 701          * {@code e1.hashCode()==e2.hashCode()} for any two Entries
 702          * {@code e1} and {@code e2}, as required by the general
 703          * contract of {@link Object#hashCode}.
 704          *
 705          * @return the hash code value for this map entry
 706          * @see    #equals
 707          */
 708         public int hashCode() {
 709             return (key   == null ? 0 :   key.hashCode()) ^
 710                    (value == null ? 0 : value.hashCode());
 711         }
 712 
 713         /**
 714          * Returns a String representation of this map entry.  This
 715          * implementation returns the string representation of this
 716          * entry's key followed by the equals character ("{@code =}")
 717          * followed by the string representation of this entry's value.
 718          *
 719          * @return a String representation of this map entry
 720          */
 721         public String toString() {
 722             return key + "=" + value;
 723         }
 724 
 725     }
 726 
 727     /**
 728      * An Entry maintaining an immutable key and value.  This class
 729      * does not support method {@code setValue}.  This class may be
 730      * convenient in methods that return thread-safe snapshots of
 731      * key-value mappings.
 732      *
 733      * @since 1.6
 734      */
 735     public static class SimpleImmutableEntry<K,V>
 736         implements Entry<K,V>, java.io.Serializable
 737     {
 738         @java.io.Serial
 739         private static final long serialVersionUID = 7138329143949025153L;
 740 
 741         private final K key;
 742         private final V value;
 743 
 744         /**
 745          * Creates an entry representing a mapping from the specified
 746          * key to the specified value.
 747          *
 748          * @param key the key represented by this entry
 749          * @param value the value represented by this entry
 750          */
 751         public SimpleImmutableEntry(K key, V value) {
 752             this.key   = key;
 753             this.value = value;
 754         }
 755 
 756         /**
 757          * Creates an entry representing the same mapping as the
 758          * specified entry.
 759          *
 760          * @param entry the entry to copy
 761          */
 762         public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
 763             this.key   = entry.getKey();
 764             this.value = entry.getValue();
 765         }
 766 
 767         /**
 768          * Returns the key corresponding to this entry.
 769          *
 770          * @return the key corresponding to this entry
 771          */
 772         public K getKey() {
 773             return key;
 774         }
 775 
 776         /**
 777          * Returns the value corresponding to this entry.
 778          *
 779          * @return the value corresponding to this entry
 780          */
 781         public V getValue() {
 782             return value;
 783         }
 784 
 785         /**
 786          * Replaces the value corresponding to this entry with the specified
 787          * value (optional operation).  This implementation simply throws
 788          * {@code UnsupportedOperationException}, as this class implements
 789          * an <i>immutable</i> map entry.
 790          *
 791          * @param value new value to be stored in this entry
 792          * @return (Does not return)
 793          * @throws UnsupportedOperationException always
 794          */
 795         public V setValue(V value) {
 796             throw new UnsupportedOperationException();
 797         }
 798 
 799         /**
 800          * Compares the specified object with this entry for equality.
 801          * Returns {@code true} if the given object is also a map entry and
 802          * the two entries represent the same mapping.  More formally, two
 803          * entries {@code e1} and {@code e2} represent the same mapping
 804          * if<pre>
 805          *   (e1.getKey()==null ?
 806          *    e2.getKey()==null :
 807          *    e1.getKey().equals(e2.getKey()))
 808          *   &amp;&amp;
 809          *   (e1.getValue()==null ?
 810          *    e2.getValue()==null :
 811          *    e1.getValue().equals(e2.getValue()))</pre>
 812          * This ensures that the {@code equals} method works properly across
 813          * different implementations of the {@code Map.Entry} interface.
 814          *
 815          * @param o object to be compared for equality with this map entry
 816          * @return {@code true} if the specified object is equal to this map
 817          *         entry
 818          * @see    #hashCode
 819          */
 820         public boolean equals(Object o) {
 821             if (!(o instanceof Map.Entry))
 822                 return false;
 823             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
 824             return eq(key, e.getKey()) && eq(value, e.getValue());
 825         }
 826 
 827         /**
 828          * Returns the hash code value for this map entry.  The hash code
 829          * of a map entry {@code e} is defined to be: <pre>
 830          *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
 831          *   (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
 832          * This ensures that {@code e1.equals(e2)} implies that
 833          * {@code e1.hashCode()==e2.hashCode()} for any two Entries
 834          * {@code e1} and {@code e2}, as required by the general
 835          * contract of {@link Object#hashCode}.
 836          *
 837          * @return the hash code value for this map entry
 838          * @see    #equals
 839          */
 840         public int hashCode() {
 841             return (key   == null ? 0 :   key.hashCode()) ^
 842                    (value == null ? 0 : value.hashCode());
 843         }
 844 
 845         /**
 846          * Returns a String representation of this map entry.  This
 847          * implementation returns the string representation of this
 848          * entry's key followed by the equals character ("{@code =}")
 849          * followed by the string representation of this entry's value.
 850          *
 851          * @return a String representation of this map entry
 852          */
 853         public String toString() {
 854             return key + "=" + value;
 855         }
 856 
 857     }
 858 
 859 }