1 /*
   2  * Copyright (c) 1997, 2012, 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 <tt>Map</tt>
  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 <tt>entrySet</tt> method, which
  35  * returns a set-view of the map's mappings.  Typically, the returned set
  36  * will, in turn, be implemented atop <tt>AbstractSet</tt>.  This set should
  37  * not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator
  38  * should not support the <tt>remove</tt> method.
  39  *
  40  * <p>To implement a modifiable map, the programmer must additionally override
  41  * this class's <tt>put</tt> method (which otherwise throws an
  42  * <tt>UnsupportedOperationException</tt>), and the iterator returned by
  43  * <tt>entrySet().iterator()</tt> must additionally implement its
  44  * <tt>remove</tt> method.
  45  *
  46  * <p>The programmer should generally provide a void (no argument) and map
  47  * constructor, as per the recommendation in the <tt>Map</tt> 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}/../technotes/guides/collections/index.html">
  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      * <p>This implementation returns <tt>entrySet().size()</tt>.
  82      */
  83     public int size() {
  84         return entrySet().size();
  85     }
  86 
  87     /**
  88      * {@inheritDoc}
  89      *
  90      * <p>This implementation returns <tt>size() == 0</tt>.
  91      */
  92     public boolean isEmpty() {
  93         return size() == 0;
  94     }
  95 
  96     /**
  97      * {@inheritDoc}
  98      *
  99      * <p>This implementation iterates over <tt>entrySet()</tt> searching
 100      * for an entry with the specified value.  If such an entry is found,
 101      * <tt>true</tt> is returned.  If the iteration terminates without
 102      * finding such an entry, <tt>false</tt> is returned.  Note that this
 103      * implementation requires linear time in the size of the map.
 104      *
 105      * @throws ClassCastException   {@inheritDoc}
 106      * @throws NullPointerException {@inheritDoc}
 107      */
 108     public boolean containsValue(Object value) {
 109         Iterator<Entry<K,V>> i = entrySet().iterator();
 110         if (value==null) {
 111             while (i.hasNext()) {
 112                 Entry<K,V> e = i.next();
 113                 if (e.getValue()==null)
 114                     return true;
 115             }
 116         } else {
 117             while (i.hasNext()) {
 118                 Entry<K,V> e = i.next();
 119                 if (value.equals(e.getValue()))
 120                     return true;
 121             }
 122         }
 123         return false;
 124     }
 125 
 126     /**
 127      * {@inheritDoc}
 128      *
 129      * <p>This implementation iterates over <tt>entrySet()</tt> searching
 130      * for an entry with the specified key.  If such an entry is found,
 131      * <tt>true</tt> is returned.  If the iteration terminates without
 132      * finding such an entry, <tt>false</tt> is returned.  Note that this
 133      * implementation requires linear time in the size of the map; many
 134      * implementations will override this method.
 135      *
 136      * @throws ClassCastException   {@inheritDoc}
 137      * @throws NullPointerException {@inheritDoc}
 138      */
 139     public boolean containsKey(Object key) {
 140         Iterator<Map.Entry<K,V>> i = entrySet().iterator();
 141         if (key==null) {
 142             while (i.hasNext()) {
 143                 Entry<K,V> e = i.next();
 144                 if (e.getKey()==null)
 145                     return true;
 146             }
 147         } else {
 148             while (i.hasNext()) {
 149                 Entry<K,V> e = i.next();
 150                 if (key.equals(e.getKey()))
 151                     return true;
 152             }
 153         }
 154         return false;
 155     }
 156 
 157     /**
 158      * {@inheritDoc}
 159      *
 160      * <p>This implementation iterates over <tt>entrySet()</tt> searching
 161      * for an entry with the specified key.  If such an entry is found,
 162      * the entry's value is returned.  If the iteration terminates without
 163      * finding such an entry, <tt>null</tt> is returned.  Note that this
 164      * implementation requires linear time in the size of the map; many
 165      * implementations will override this method.
 166      *
 167      * @throws ClassCastException            {@inheritDoc}
 168      * @throws NullPointerException          {@inheritDoc}
 169      */
 170     public V get(Object key) {
 171         Iterator<Entry<K,V>> i = entrySet().iterator();
 172         if (key==null) {
 173             while (i.hasNext()) {
 174                 Entry<K,V> e = i.next();
 175                 if (e.getKey()==null)
 176                     return e.getValue();
 177             }
 178         } else {
 179             while (i.hasNext()) {
 180                 Entry<K,V> e = i.next();
 181                 if (key.equals(e.getKey()))
 182                     return e.getValue();
 183             }
 184         }
 185         return null;
 186     }
 187 
 188 
 189     // Modification Operations
 190 
 191     /**
 192      * {@inheritDoc}
 193      *
 194      * <p>This implementation always throws an
 195      * <tt>UnsupportedOperationException</tt>.
 196      *
 197      * @throws UnsupportedOperationException {@inheritDoc}
 198      * @throws ClassCastException            {@inheritDoc}
 199      * @throws NullPointerException          {@inheritDoc}
 200      * @throws IllegalArgumentException      {@inheritDoc}
 201      */
 202     public V put(K key, V value) {
 203         throw new UnsupportedOperationException();
 204     }
 205 
 206     /**
 207      * {@inheritDoc}
 208      *
 209      * <p>This implementation iterates over <tt>entrySet()</tt> searching for an
 210      * entry with the specified key.  If such an entry is found, its value is
 211      * obtained with its <tt>getValue</tt> operation, the entry is removed
 212      * from the collection (and the backing map) with the iterator's
 213      * <tt>remove</tt> operation, and the saved value is returned.  If the
 214      * iteration terminates without finding such an entry, <tt>null</tt> is
 215      * returned.  Note that this implementation requires linear time in the
 216      * size of the map; many implementations will override this method.
 217      *
 218      * <p>Note that this implementation throws an
 219      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
 220      * iterator does not support the <tt>remove</tt> method and this map
 221      * contains a mapping for the specified key.
 222      *
 223      * @throws UnsupportedOperationException {@inheritDoc}
 224      * @throws ClassCastException            {@inheritDoc}
 225      * @throws NullPointerException          {@inheritDoc}
 226      */
 227     public V remove(Object key) {
 228         Iterator<Entry<K,V>> i = entrySet().iterator();
 229         Entry<K,V> correctEntry = null;
 230         if (key==null) {
 231             while (correctEntry==null && i.hasNext()) {
 232                 Entry<K,V> e = i.next();
 233                 if (e.getKey()==null)
 234                     correctEntry = e;
 235             }
 236         } else {
 237             while (correctEntry==null && i.hasNext()) {
 238                 Entry<K,V> e = i.next();
 239                 if (key.equals(e.getKey()))
 240                     correctEntry = e;
 241             }
 242         }
 243 
 244         V oldValue = null;
 245         if (correctEntry !=null) {
 246             oldValue = correctEntry.getValue();
 247             i.remove();
 248         }
 249         return oldValue;
 250     }
 251 
 252 
 253     // Bulk Operations
 254 
 255     /**
 256      * {@inheritDoc}
 257      *
 258      * <p>This implementation iterates over the specified map's
 259      * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
 260      * operation once for each entry returned by the iteration.
 261      *
 262      * <p>Note that this implementation throws an
 263      * <tt>UnsupportedOperationException</tt> if this map does not support
 264      * the <tt>put</tt> operation and the specified map is nonempty.
 265      *
 266      * @throws UnsupportedOperationException {@inheritDoc}
 267      * @throws ClassCastException            {@inheritDoc}
 268      * @throws NullPointerException          {@inheritDoc}
 269      * @throws IllegalArgumentException      {@inheritDoc}
 270      */
 271     public void putAll(Map<? extends K, ? extends V> m) {
 272         for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
 273             put(e.getKey(), e.getValue());
 274     }
 275 
 276     /**
 277      * {@inheritDoc}
 278      *
 279      * <p>This implementation calls <tt>entrySet().clear()</tt>.
 280      *
 281      * <p>Note that this implementation throws an
 282      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
 283      * does not support the <tt>clear</tt> operation.
 284      *
 285      * @throws UnsupportedOperationException {@inheritDoc}
 286      */
 287     public void clear() {
 288         entrySet().clear();
 289     }
 290 
 291 
 292     // Views
 293 
 294     /**
 295      * Each of these fields are initialized to contain an instance of the
 296      * appropriate view the first time this view is requested.  The views are
 297      * stateless, so there's no reason to create more than one of each.
 298      */
 299     transient volatile Set<K>        keySet = null;
 300     transient volatile Collection<V> values = null;
 301 
 302     /**
 303      * {@inheritDoc}
 304      *
 305      * <p>This implementation returns a set that subclasses {@link AbstractSet}.
 306      * The subclass's iterator method returns a "wrapper object" over this
 307      * map's <tt>entrySet()</tt> iterator.  The <tt>size</tt> method
 308      * delegates to this map's <tt>size</tt> method and the
 309      * <tt>contains</tt> method delegates to this map's
 310      * <tt>containsKey</tt> method.
 311      *
 312      * <p>The set is created the first time this method is called,
 313      * and returned in response to all subsequent calls.  No synchronization
 314      * is performed, so there is a slight chance that multiple calls to this
 315      * method will not all return the same set.
 316      */
 317     public Set<K> keySet() {
 318         if (keySet == null) {
 319             keySet = new AbstractSet<K>() {
 320                 public Iterator<K> iterator() {
 321                     return new Iterator<K>() {
 322                         private Iterator<Entry<K,V>> i = entrySet().iterator();
 323 
 324                         public boolean hasNext() {
 325                             return i.hasNext();
 326                         }
 327 
 328                         public K next() {
 329                             return i.next().getKey();
 330                         }
 331 
 332                         public void remove() {
 333                             i.remove();
 334                         }
 335                     };
 336                 }
 337 
 338                 public int size() {
 339                     return AbstractMap.this.size();
 340                 }
 341 
 342                 public boolean isEmpty() {
 343                     return AbstractMap.this.isEmpty();
 344                 }
 345 
 346                 public void clear() {
 347                     AbstractMap.this.clear();
 348                 }
 349 
 350                 public boolean contains(Object k) {
 351                     return AbstractMap.this.containsKey(k);
 352                 }
 353             };
 354         }
 355         return keySet;
 356     }
 357 
 358     /**
 359      * {@inheritDoc}
 360      *
 361      * <p>This implementation returns a collection that subclasses {@link
 362      * AbstractCollection}.  The subclass's iterator method returns a
 363      * "wrapper object" over this map's <tt>entrySet()</tt> iterator.
 364      * The <tt>size</tt> method delegates to this map's <tt>size</tt>
 365      * method and the <tt>contains</tt> method delegates to this map's
 366      * <tt>containsValue</tt> method.
 367      *
 368      * <p>The collection is created the first time this method is called, and
 369      * returned in response to all subsequent calls.  No synchronization is
 370      * performed, so there is a slight chance that multiple calls to this
 371      * method will not all return the same collection.
 372      */
 373     public Collection<V> values() {
 374         if (values == null) {
 375             values = new AbstractCollection<V>() {
 376                 public Iterator<V> iterator() {
 377                     return new Iterator<V>() {
 378                         private Iterator<Entry<K,V>> i = entrySet().iterator();
 379 
 380                         public boolean hasNext() {
 381                             return i.hasNext();
 382                         }
 383 
 384                         public V next() {
 385                             return i.next().getValue();
 386                         }
 387 
 388                         public void remove() {
 389                             i.remove();
 390                         }
 391                     };
 392                 }
 393 
 394                 public int size() {
 395                     return AbstractMap.this.size();
 396                 }
 397 
 398                 public boolean isEmpty() {
 399                     return AbstractMap.this.isEmpty();
 400                 }
 401 
 402                 public void clear() {
 403                     AbstractMap.this.clear();
 404                 }
 405 
 406                 public boolean contains(Object v) {
 407                     return AbstractMap.this.containsValue(v);
 408                 }
 409             };
 410         }
 411         return values;
 412     }
 413 
 414     public abstract Set<Entry<K,V>> entrySet();
 415 
 416 
 417     // Comparison and hashing
 418 
 419     /**
 420      * Compares the specified object with this map for equality.  Returns
 421      * <tt>true</tt> if the given object is also a map and the two maps
 422      * represent the same mappings.  More formally, two maps <tt>m1</tt> and
 423      * <tt>m2</tt> represent the same mappings if
 424      * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the
 425      * <tt>equals</tt> method works properly across different implementations
 426      * of the <tt>Map</tt> interface.
 427      *
 428      * <p>This implementation first checks if the specified object is this map;
 429      * if so it returns <tt>true</tt>.  Then, it checks if the specified
 430      * object is a map whose size is identical to the size of this map; if
 431      * not, it returns <tt>false</tt>.  If so, it iterates over this map's
 432      * <tt>entrySet</tt> collection, and checks that the specified map
 433      * contains each mapping that this map contains.  If the specified map
 434      * fails to contain such a mapping, <tt>false</tt> is returned.  If the
 435      * iteration completes, <tt>true</tt> is returned.
 436      *
 437      * @param o object to be compared for equality with this map
 438      * @return <tt>true</tt> if the specified object is equal to this map
 439      */
 440     public boolean equals(Object o) {
 441         if (o == this)
 442             return true;
 443 
 444         if (!(o instanceof Map))
 445             return false;
 446         Map<?,?> m = (Map<?,?>) o;
 447         if (m.size() != size())
 448             return false;
 449 
 450         try {
 451             Iterator<Entry<K,V>> i = entrySet().iterator();
 452             while (i.hasNext()) {
 453                 Entry<K,V> e = i.next();
 454                 K key = e.getKey();
 455                 V value = e.getValue();
 456                 if (value == null) {
 457                     if (!(m.get(key)==null && m.containsKey(key)))
 458                         return false;
 459                 } else {
 460                     if (!value.equals(m.get(key)))
 461                         return false;
 462                 }
 463             }
 464         } catch (ClassCastException unused) {
 465             return false;
 466         } catch (NullPointerException unused) {
 467             return false;
 468         }
 469 
 470         return true;
 471     }
 472 
 473     /**
 474      * Returns the hash code value for this map.  The hash code of a map is
 475      * defined to be the sum of the hash codes of each entry in the map's
 476      * <tt>entrySet()</tt> view.  This ensures that <tt>m1.equals(m2)</tt>
 477      * implies that <tt>m1.hashCode()==m2.hashCode()</tt> for any two maps
 478      * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of
 479      * {@link Object#hashCode}.
 480      *
 481      * <p>This implementation iterates over <tt>entrySet()</tt>, calling
 482      * {@link Map.Entry#hashCode hashCode()} on each element (entry) in the
 483      * set, and adding up the results.
 484      *
 485      * @return the hash code value for this map
 486      * @see Map.Entry#hashCode()
 487      * @see Object#equals(Object)
 488      * @see Set#equals(Object)
 489      */
 490     public int hashCode() {
 491         int h = 0;
 492         Iterator<Entry<K,V>> i = entrySet().iterator();
 493         while (i.hasNext())
 494             h += i.next().hashCode();
 495         return h;
 496     }
 497 
 498     /**
 499      * Returns a string representation of this map.  The string representation
 500      * consists of a list of key-value mappings in the order returned by the
 501      * map's <tt>entrySet</tt> view's iterator, enclosed in braces
 502      * (<tt>"{}"</tt>).  Adjacent mappings are separated by the characters
 503      * <tt>", "</tt> (comma and space).  Each key-value mapping is rendered as
 504      * the key followed by an equals sign (<tt>"="</tt>) followed by the
 505      * associated value.  Keys and values are converted to strings as by
 506      * {@link String#valueOf(Object)}.
 507      *
 508      * @return a string representation of this map
 509      */
 510     public String toString() {
 511         Iterator<Entry<K,V>> i = entrySet().iterator();
 512         if (! i.hasNext())
 513             return "{}";
 514 
 515         StringBuilder sb = new StringBuilder();
 516         sb.append('{');
 517         for (;;) {
 518             Entry<K,V> e = i.next();
 519             K key = e.getKey();
 520             V value = e.getValue();
 521             sb.append(key   == this ? "(this Map)" : key);
 522             sb.append('=');
 523             sb.append(value == this ? "(this Map)" : value);
 524             if (! i.hasNext())
 525                 return sb.append('}').toString();
 526             sb.append(',').append(' ');
 527         }
 528     }
 529 
 530     /**
 531      * Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys
 532      * and values themselves are not cloned.
 533      *
 534      * @return a shallow copy of this map
 535      */
 536     protected Object clone() throws CloneNotSupportedException {
 537         AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
 538         result.keySet = null;
 539         result.values = null;
 540         return result;
 541     }
 542 
 543     /**
 544      * Utility method for SimpleEntry and SimpleImmutableEntry.
 545      * Test for equality, checking for nulls.
 546      */
 547     private static boolean eq(Object o1, Object o2) {
 548         return o1 == null ? o2 == null : o1.equals(o2);
 549     }
 550 
 551     // Implementation Note: SimpleEntry and SimpleImmutableEntry
 552     // are distinct unrelated classes, even though they share
 553     // some code. Since you can't add or subtract final-ness
 554     // of a field in a subclass, they can't share representations,
 555     // and the amount of duplicated code is too small to warrant
 556     // exposing a common abstract class.
 557 
 558 
 559     /**
 560      * An Entry maintaining a key and a value.  The value may be
 561      * changed using the <tt>setValue</tt> method.  This class
 562      * facilitates the process of building custom map
 563      * implementations. For example, it may be convenient to return
 564      * arrays of <tt>SimpleEntry</tt> instances in method
 565      * <tt>Map.entrySet().toArray</tt>.
 566      *
 567      * @since 1.6
 568      */
 569     public static class SimpleEntry<K,V>
 570         implements Entry<K,V>, java.io.Serializable
 571     {
 572         private static final long serialVersionUID = -8499721149061103585L;
 573 
 574         private final K key;
 575         private V value;
 576 
 577         /**
 578          * Creates an entry representing a mapping from the specified
 579          * key to the specified value.
 580          *
 581          * @param key the key represented by this entry
 582          * @param value the value represented by this entry
 583          */
 584         public SimpleEntry(K key, V value) {
 585             this.key   = key;
 586             this.value = value;
 587         }
 588 
 589         /**
 590          * Creates an entry representing the same mapping as the
 591          * specified entry.
 592          *
 593          * @param entry the entry to copy
 594          */
 595         public SimpleEntry(Entry<? extends K, ? extends V> entry) {
 596             this.key   = entry.getKey();
 597             this.value = entry.getValue();
 598         }
 599 
 600         /**
 601          * Returns the key corresponding to this entry.
 602          *
 603          * @return the key corresponding to this entry
 604          */
 605         public K getKey() {
 606             return key;
 607         }
 608 
 609         /**
 610          * Returns the value corresponding to this entry.
 611          *
 612          * @return the value corresponding to this entry
 613          */
 614         public V getValue() {
 615             return value;
 616         }
 617 
 618         /**
 619          * Replaces the value corresponding to this entry with the specified
 620          * value.
 621          *
 622          * @param value new value to be stored in this entry
 623          * @return the old value corresponding to the entry
 624          */
 625         public V setValue(V value) {
 626             V oldValue = this.value;
 627             this.value = value;
 628             return oldValue;
 629         }
 630 
 631         /**
 632          * Compares the specified object with this entry for equality.
 633          * Returns {@code true} if the given object is also a map entry and
 634          * the two entries represent the same mapping.  More formally, two
 635          * entries {@code e1} and {@code e2} represent the same mapping
 636          * if<pre>
 637          *   (e1.getKey()==null ?
 638          *    e2.getKey()==null :
 639          *    e1.getKey().equals(e2.getKey()))
 640          *   &amp;&amp;
 641          *   (e1.getValue()==null ?
 642          *    e2.getValue()==null :
 643          *    e1.getValue().equals(e2.getValue()))</pre>
 644          * This ensures that the {@code equals} method works properly across
 645          * different implementations of the {@code Map.Entry} interface.
 646          *
 647          * @param o object to be compared for equality with this map entry
 648          * @return {@code true} if the specified object is equal to this map
 649          *         entry
 650          * @see    #hashCode
 651          */
 652         public boolean equals(Object o) {
 653             if (!(o instanceof Map.Entry))
 654                 return false;
 655             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
 656             return eq(key, e.getKey()) && eq(value, e.getValue());
 657         }
 658 
 659         /**
 660          * Returns the hash code value for this map entry.  The hash code
 661          * of a map entry {@code e} is defined to be: <pre>
 662          *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
 663          *   (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
 664          * This ensures that {@code e1.equals(e2)} implies that
 665          * {@code e1.hashCode()==e2.hashCode()} for any two Entries
 666          * {@code e1} and {@code e2}, as required by the general
 667          * contract of {@link Object#hashCode}.
 668          *
 669          * @return the hash code value for this map entry
 670          * @see    #equals
 671          */
 672         public int hashCode() {
 673             return (key   == null ? 0 :   key.hashCode()) ^
 674                    (value == null ? 0 : value.hashCode());
 675         }
 676 
 677         /**
 678          * Returns a String representation of this map entry.  This
 679          * implementation returns the string representation of this
 680          * entry's key followed by the equals character ("<tt>=</tt>")
 681          * followed by the string representation of this entry's value.
 682          *
 683          * @return a String representation of this map entry
 684          */
 685         public String toString() {
 686             return key + "=" + value;
 687         }
 688 
 689     }
 690 
 691     /**
 692      * An Entry maintaining an immutable key and value.  This class
 693      * does not support method <tt>setValue</tt>.  This class may be
 694      * convenient in methods that return thread-safe snapshots of
 695      * key-value mappings.
 696      *
 697      * @since 1.6
 698      */
 699     public static class SimpleImmutableEntry<K,V>
 700         implements Entry<K,V>, java.io.Serializable
 701     {
 702         private static final long serialVersionUID = 7138329143949025153L;
 703 
 704         private final K key;
 705         private final V value;
 706 
 707         /**
 708          * Creates an entry representing a mapping from the specified
 709          * key to the specified value.
 710          *
 711          * @param key the key represented by this entry
 712          * @param value the value represented by this entry
 713          */
 714         public SimpleImmutableEntry(K key, V value) {
 715             this.key   = key;
 716             this.value = value;
 717         }
 718 
 719         /**
 720          * Creates an entry representing the same mapping as the
 721          * specified entry.
 722          *
 723          * @param entry the entry to copy
 724          */
 725         public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
 726             this.key   = entry.getKey();
 727             this.value = entry.getValue();
 728         }
 729 
 730         /**
 731          * Returns the key corresponding to this entry.
 732          *
 733          * @return the key corresponding to this entry
 734          */
 735         public K getKey() {
 736             return key;
 737         }
 738 
 739         /**
 740          * Returns the value corresponding to this entry.
 741          *
 742          * @return the value corresponding to this entry
 743          */
 744         public V getValue() {
 745             return value;
 746         }
 747 
 748         /**
 749          * Replaces the value corresponding to this entry with the specified
 750          * value (optional operation).  This implementation simply throws
 751          * <tt>UnsupportedOperationException</tt>, as this class implements
 752          * an <i>immutable</i> map entry.
 753          *
 754          * @param value new value to be stored in this entry
 755          * @return (Does not return)
 756          * @throws UnsupportedOperationException always
 757          */
 758         public V setValue(V value) {
 759             throw new UnsupportedOperationException();
 760         }
 761 
 762         /**
 763          * Compares the specified object with this entry for equality.
 764          * Returns {@code true} if the given object is also a map entry and
 765          * the two entries represent the same mapping.  More formally, two
 766          * entries {@code e1} and {@code e2} represent the same mapping
 767          * if<pre>
 768          *   (e1.getKey()==null ?
 769          *    e2.getKey()==null :
 770          *    e1.getKey().equals(e2.getKey()))
 771          *   &amp;&amp;
 772          *   (e1.getValue()==null ?
 773          *    e2.getValue()==null :
 774          *    e1.getValue().equals(e2.getValue()))</pre>
 775          * This ensures that the {@code equals} method works properly across
 776          * different implementations of the {@code Map.Entry} interface.
 777          *
 778          * @param o object to be compared for equality with this map entry
 779          * @return {@code true} if the specified object is equal to this map
 780          *         entry
 781          * @see    #hashCode
 782          */
 783         public boolean equals(Object o) {
 784             if (!(o instanceof Map.Entry))
 785                 return false;
 786             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
 787             return eq(key, e.getKey()) && eq(value, e.getValue());
 788         }
 789 
 790         /**
 791          * Returns the hash code value for this map entry.  The hash code
 792          * of a map entry {@code e} is defined to be: <pre>
 793          *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
 794          *   (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
 795          * This ensures that {@code e1.equals(e2)} implies that
 796          * {@code e1.hashCode()==e2.hashCode()} for any two Entries
 797          * {@code e1} and {@code e2}, as required by the general
 798          * contract of {@link Object#hashCode}.
 799          *
 800          * @return the hash code value for this map entry
 801          * @see    #equals
 802          */
 803         public int hashCode() {
 804             return (key   == null ? 0 :   key.hashCode()) ^
 805                    (value == null ? 0 : value.hashCode());
 806         }
 807 
 808         /**
 809          * Returns a String representation of this map entry.  This
 810          * implementation returns the string representation of this
 811          * entry's key followed by the equals character ("<tt>=</tt>")
 812          * followed by the string representation of this entry's value.
 813          *
 814          * @return a String representation of this map entry
 815          */
 816         public String toString() {
 817             return key + "=" + value;
 818         }
 819 
 820     }
 821 
 822 }