1 /*
   2  * Copyright (c) 1997, 2013, 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      * @implSpec
  82      * This implementation returns <tt>entrySet().size()</tt>.
  83      */
  84     public int size() {
  85         return entrySet().size();
  86     }
  87 
  88     /**
  89      * {@inheritDoc}
  90      *
  91      * @implSpec
  92      * This implementation returns <tt>size() == 0</tt>.
  93      */
  94     public boolean isEmpty() {
  95         return size() == 0;
  96     }
  97 
  98     /**
  99      * {@inheritDoc}
 100      *
 101      * @implSpec
 102      * This implementation iterates over <tt>entrySet()</tt> searching
 103      * for an entry with the specified value.  If such an entry is found,
 104      * <tt>true</tt> is returned.  If the iteration terminates without
 105      * finding such an entry, <tt>false</tt> 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 <tt>entrySet()</tt> searching
 134      * for an entry with the specified key.  If such an entry is found,
 135      * <tt>true</tt> is returned.  If the iteration terminates without
 136      * finding such an entry, <tt>false</tt> 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 <tt>entrySet()</tt> 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, <tt>null</tt> 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      * <tt>UnsupportedOperationException</tt>.
 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 <tt>entrySet()</tt> searching for an
 217      * entry with the specified key.  If such an entry is found, its value is
 218      * obtained with its <tt>getValue</tt> operation, the entry is removed
 219      * from the collection (and the backing map) with the iterator's
 220      * <tt>remove</tt> operation, and the saved value is returned.  If the
 221      * iteration terminates without finding such an entry, <tt>null</tt> 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      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
 227      * iterator does not support the <tt>remove</tt> 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      * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
 268      * operation once for each entry returned by the iteration.
 269      *
 270      * <p>Note that this implementation throws an
 271      * <tt>UnsupportedOperationException</tt> if this map does not support
 272      * the <tt>put</tt> 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 <tt>entrySet().clear()</tt>.
 289      *
 290      * <p>Note that this implementation throws an
 291      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
 292      * does not support the <tt>clear</tt> 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     transient volatile Set<K>        keySet;
 309     transient volatile Collection<V> values;
 310 
 311     /**
 312      * {@inheritDoc}
 313      *
 314      * @implSpec
 315      * This implementation returns a set that subclasses {@link AbstractSet}.
 316      * The subclass's iterator method returns a "wrapper object" over this
 317      * map's <tt>entrySet()</tt> iterator.  The <tt>size</tt> method
 318      * delegates to this map's <tt>size</tt> method and the
 319      * <tt>contains</tt> method delegates to this map's
 320      * <tt>containsKey</tt> method.
 321      *
 322      * <p>The set is created the first time this method is called,
 323      * and returned in response to all subsequent calls.  No synchronization
 324      * is performed, so there is a slight chance that multiple calls to this
 325      * method will not all return the same set.
 326      */
 327     public Set<K> keySet() {
 328         if (keySet == null) {
 329             keySet = new AbstractSet<K>() {
 330                 public Iterator<K> iterator() {
 331                     return new Iterator<K>() {
 332                         private Iterator<Entry<K,V>> i = entrySet().iterator();
 333 
 334                         public boolean hasNext() {
 335                             return i.hasNext();
 336                         }
 337 
 338                         public K next() {
 339                             return i.next().getKey();
 340                         }
 341 
 342                         public void remove() {
 343                             i.remove();
 344                         }
 345                     };
 346                 }
 347 
 348                 public int size() {
 349                     return AbstractMap.this.size();
 350                 }
 351 
 352                 public boolean isEmpty() {
 353                     return AbstractMap.this.isEmpty();
 354                 }
 355 
 356                 public void clear() {
 357                     AbstractMap.this.clear();
 358                 }
 359 
 360                 public boolean contains(Object k) {
 361                     return AbstractMap.this.containsKey(k);
 362                 }
 363             };
 364         }
 365         return keySet;
 366     }
 367 
 368     /**
 369      * {@inheritDoc}
 370      *
 371      * @implSpec
 372      * This implementation returns a collection that subclasses {@link
 373      * AbstractCollection}.  The subclass's iterator method returns a
 374      * "wrapper object" over this map's <tt>entrySet()</tt> iterator.
 375      * The <tt>size</tt> method delegates to this map's <tt>size</tt>
 376      * method and the <tt>contains</tt> method delegates to this map's
 377      * <tt>containsValue</tt> method.
 378      *
 379      * <p>The collection is created the first time this method is called, and
 380      * returned in response to all subsequent calls.  No synchronization is
 381      * performed, so there is a slight chance that multiple calls to this
 382      * method will not all return the same collection.
 383      */
 384     public Collection<V> values() {
 385         if (values == null) {
 386             values = new AbstractCollection<V>() {
 387                 public Iterator<V> iterator() {
 388                     return new Iterator<V>() {
 389                         private Iterator<Entry<K,V>> i = entrySet().iterator();
 390 
 391                         public boolean hasNext() {
 392                             return i.hasNext();
 393                         }
 394 
 395                         public V next() {
 396                             return i.next().getValue();
 397                         }
 398 
 399                         public void remove() {
 400                             i.remove();
 401                         }
 402                     };
 403                 }
 404 
 405                 public int size() {
 406                     return AbstractMap.this.size();
 407                 }
 408 
 409                 public boolean isEmpty() {
 410                     return AbstractMap.this.isEmpty();
 411                 }
 412 
 413                 public void clear() {
 414                     AbstractMap.this.clear();
 415                 }
 416 
 417                 public boolean contains(Object v) {
 418                     return AbstractMap.this.containsValue(v);
 419                 }
 420             };
 421         }
 422         return values;
 423     }
 424 
 425     public abstract Set<Entry<K,V>> entrySet();
 426 
 427 
 428     // Comparison and hashing
 429 
 430     /**
 431      * Compares the specified object with this map for equality.  Returns
 432      * <tt>true</tt> if the given object is also a map and the two maps
 433      * represent the same mappings.  More formally, two maps <tt>m1</tt> and
 434      * <tt>m2</tt> represent the same mappings if
 435      * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the
 436      * <tt>equals</tt> method works properly across different implementations
 437      * of the <tt>Map</tt> interface.
 438      *
 439      * @implSpec
 440      * This implementation first checks if the specified object is this map;
 441      * if so it returns <tt>true</tt>.  Then, it checks if the specified
 442      * object is a map whose size is identical to the size of this map; if
 443      * not, it returns <tt>false</tt>.  If so, it iterates over this map's
 444      * <tt>entrySet</tt> collection, and checks that the specified map
 445      * contains each mapping that this map contains.  If the specified map
 446      * fails to contain such a mapping, <tt>false</tt> is returned.  If the
 447      * iteration completes, <tt>true</tt> is returned.
 448      *
 449      * @param o object to be compared for equality with this map
 450      * @return <tt>true</tt> if the specified object is equal to this map
 451      */
 452     public boolean equals(Object o) {
 453         if (o == this)
 454             return true;
 455 
 456         if (!(o instanceof Map))
 457             return false;
 458         Map<?,?> m = (Map<?,?>) o;
 459         if (m.size() != size())
 460             return false;
 461 
 462         try {
 463             for (Entry<K, V> e : entrySet()) {
 464                 K key = e.getKey();
 465                 V value = e.getValue();
 466                 if (value == null) {
 467                     if (!(m.get(key) == null && m.containsKey(key)))
 468                         return false;
 469                 } else {
 470                     if (!value.equals(m.get(key)))
 471                         return false;
 472                 }
 473             }
 474         } catch (ClassCastException unused) {
 475             return false;
 476         } catch (NullPointerException unused) {
 477             return false;
 478         }
 479 
 480         return true;
 481     }
 482 
 483     /**
 484      * Returns the hash code value for this map.  The hash code of a map is
 485      * defined to be the sum of the hash codes of each entry in the map's
 486      * <tt>entrySet()</tt> view.  This ensures that <tt>m1.equals(m2)</tt>
 487      * implies that <tt>m1.hashCode()==m2.hashCode()</tt> for any two maps
 488      * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of
 489      * {@link Object#hashCode}.
 490      *
 491      * @implSpec
 492      * This implementation iterates over <tt>entrySet()</tt>, calling
 493      * {@link Map.Entry#hashCode hashCode()} on each element (entry) in the
 494      * set, and adding up the results.
 495      *
 496      * @return the hash code value for this map
 497      * @see Map.Entry#hashCode()
 498      * @see Object#equals(Object)
 499      * @see Set#equals(Object)
 500      */
 501     public int hashCode() {
 502         int h = 0;
 503         for (Entry<K, V> entry : entrySet())
 504             h += entry.hashCode();
 505         return h;
 506     }
 507 
 508     /**
 509      * Returns a string representation of this map.  The string representation
 510      * consists of a list of key-value mappings in the order returned by the
 511      * map's <tt>entrySet</tt> view's iterator, enclosed in braces
 512      * (<tt>"{}"</tt>).  Adjacent mappings are separated by the characters
 513      * <tt>", "</tt> (comma and space).  Each key-value mapping is rendered as
 514      * the key followed by an equals sign (<tt>"="</tt>) followed by the
 515      * associated value.  Keys and values are converted to strings as by
 516      * {@link String#valueOf(Object)}.
 517      *
 518      * @return a string representation of this map
 519      */
 520     public String toString() {
 521         Iterator<Entry<K,V>> i = entrySet().iterator();
 522         if (! i.hasNext())
 523             return "{}";
 524 
 525         StringBuilder sb = new StringBuilder();
 526         sb.append('{');
 527         for (;;) {
 528             Entry<K,V> e = i.next();
 529             K key = e.getKey();
 530             V value = e.getValue();
 531             sb.append(key   == this ? "(this Map)" : key);
 532             sb.append('=');
 533             sb.append(value == this ? "(this Map)" : value);
 534             if (! i.hasNext())
 535                 return sb.append('}').toString();
 536             sb.append(',').append(' ');
 537         }
 538     }
 539 
 540     /**
 541      * Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys
 542      * and values themselves are not cloned.
 543      *
 544      * @return a shallow copy of this map
 545      */
 546     protected Object clone() throws CloneNotSupportedException {
 547         AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
 548         result.keySet = null;
 549         result.values = null;
 550         return result;
 551     }
 552 
 553     /**
 554      * Utility method for SimpleEntry and SimpleImmutableEntry.
 555      * Test for equality, checking for nulls.
 556      *
 557      * NB: Do not replace with Object.equals until JDK-8015417 is resolved.
 558      */
 559     private static boolean eq(Object o1, Object o2) {
 560         return o1 == null ? o2 == null : o1.equals(o2);
 561     }
 562 
 563     // Implementation Note: SimpleEntry and SimpleImmutableEntry
 564     // are distinct unrelated classes, even though they share
 565     // some code. Since you can't add or subtract final-ness
 566     // of a field in a subclass, they can't share representations,
 567     // and the amount of duplicated code is too small to warrant
 568     // exposing a common abstract class.
 569 
 570 
 571     /**
 572      * An Entry maintaining a key and a value.  The value may be
 573      * changed using the <tt>setValue</tt> method.  This class
 574      * facilitates the process of building custom map
 575      * implementations. For example, it may be convenient to return
 576      * arrays of <tt>SimpleEntry</tt> instances in method
 577      * <tt>Map.entrySet().toArray</tt>.
 578      *
 579      * @since 1.6
 580      */
 581     public static class SimpleEntry<K,V>
 582         implements Entry<K,V>, java.io.Serializable
 583     {
 584         private static final long serialVersionUID = -8499721149061103585L;
 585 
 586         private final K key;
 587         private V value;
 588 
 589         /**
 590          * Creates an entry representing a mapping from the specified
 591          * key to the specified value.
 592          *
 593          * @param key the key represented by this entry
 594          * @param value the value represented by this entry
 595          */
 596         public SimpleEntry(K key, V value) {
 597             this.key   = key;
 598             this.value = value;
 599         }
 600 
 601         /**
 602          * Creates an entry representing the same mapping as the
 603          * specified entry.
 604          *
 605          * @param entry the entry to copy
 606          */
 607         public SimpleEntry(Entry<? extends K, ? extends V> entry) {
 608             this.key   = entry.getKey();
 609             this.value = entry.getValue();
 610         }
 611 
 612         /**
 613          * Returns the key corresponding to this entry.
 614          *
 615          * @return the key corresponding to this entry
 616          */
 617         public K getKey() {
 618             return key;
 619         }
 620 
 621         /**
 622          * Returns the value corresponding to this entry.
 623          *
 624          * @return the value corresponding to this entry
 625          */
 626         public V getValue() {
 627             return value;
 628         }
 629 
 630         /**
 631          * Replaces the value corresponding to this entry with the specified
 632          * value.
 633          *
 634          * @param value new value to be stored in this entry
 635          * @return the old value corresponding to the entry
 636          */
 637         public V setValue(V value) {
 638             V oldValue = this.value;
 639             this.value = value;
 640             return oldValue;
 641         }
 642 
 643         /**
 644          * Compares the specified object with this entry for equality.
 645          * Returns {@code true} if the given object is also a map entry and
 646          * the two entries represent the same mapping.  More formally, two
 647          * entries {@code e1} and {@code e2} represent the same mapping
 648          * if<pre>
 649          *   (e1.getKey()==null ?
 650          *    e2.getKey()==null :
 651          *    e1.getKey().equals(e2.getKey()))
 652          *   &amp;&amp;
 653          *   (e1.getValue()==null ?
 654          *    e2.getValue()==null :
 655          *    e1.getValue().equals(e2.getValue()))</pre>
 656          * This ensures that the {@code equals} method works properly across
 657          * different implementations of the {@code Map.Entry} interface.
 658          *
 659          * @param o object to be compared for equality with this map entry
 660          * @return {@code true} if the specified object is equal to this map
 661          *         entry
 662          * @see    #hashCode
 663          */
 664         public boolean equals(Object o) {
 665             if (!(o instanceof Map.Entry))
 666                 return false;
 667             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
 668             return eq(key, e.getKey()) && eq(value, e.getValue());
 669         }
 670 
 671         /**
 672          * Returns the hash code value for this map entry.  The hash code
 673          * of a map entry {@code e} is defined to be: <pre>
 674          *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
 675          *   (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
 676          * This ensures that {@code e1.equals(e2)} implies that
 677          * {@code e1.hashCode()==e2.hashCode()} for any two Entries
 678          * {@code e1} and {@code e2}, as required by the general
 679          * contract of {@link Object#hashCode}.
 680          *
 681          * @return the hash code value for this map entry
 682          * @see    #equals
 683          */
 684         public int hashCode() {
 685             return (key   == null ? 0 :   key.hashCode()) ^
 686                    (value == null ? 0 : value.hashCode());
 687         }
 688 
 689         /**
 690          * Returns a String representation of this map entry.  This
 691          * implementation returns the string representation of this
 692          * entry's key followed by the equals character ("<tt>=</tt>")
 693          * followed by the string representation of this entry's value.
 694          *
 695          * @return a String representation of this map entry
 696          */
 697         public String toString() {
 698             return key + "=" + value;
 699         }
 700 
 701     }
 702 
 703     /**
 704      * An Entry maintaining an immutable key and value.  This class
 705      * does not support method <tt>setValue</tt>.  This class may be
 706      * convenient in methods that return thread-safe snapshots of
 707      * key-value mappings.
 708      *
 709      * @since 1.6
 710      */
 711     public static class SimpleImmutableEntry<K,V>
 712         implements Entry<K,V>, java.io.Serializable
 713     {
 714         private static final long serialVersionUID = 7138329143949025153L;
 715 
 716         private final K key;
 717         private final V value;
 718 
 719         /**
 720          * Creates an entry representing a mapping from the specified
 721          * key to the specified value.
 722          *
 723          * @param key the key represented by this entry
 724          * @param value the value represented by this entry
 725          */
 726         public SimpleImmutableEntry(K key, V value) {
 727             this.key   = key;
 728             this.value = value;
 729         }
 730 
 731         /**
 732          * Creates an entry representing the same mapping as the
 733          * specified entry.
 734          *
 735          * @param entry the entry to copy
 736          */
 737         public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
 738             this.key   = entry.getKey();
 739             this.value = entry.getValue();
 740         }
 741 
 742         /**
 743          * Returns the key corresponding to this entry.
 744          *
 745          * @return the key corresponding to this entry
 746          */
 747         public K getKey() {
 748             return key;
 749         }
 750 
 751         /**
 752          * Returns the value corresponding to this entry.
 753          *
 754          * @return the value corresponding to this entry
 755          */
 756         public V getValue() {
 757             return value;
 758         }
 759 
 760         /**
 761          * Replaces the value corresponding to this entry with the specified
 762          * value (optional operation).  This implementation simply throws
 763          * <tt>UnsupportedOperationException</tt>, as this class implements
 764          * an <i>immutable</i> map entry.
 765          *
 766          * @param value new value to be stored in this entry
 767          * @return (Does not return)
 768          * @throws UnsupportedOperationException always
 769          */
 770         public V setValue(V value) {
 771             throw new UnsupportedOperationException();
 772         }
 773 
 774         /**
 775          * Compares the specified object with this entry for equality.
 776          * Returns {@code true} if the given object is also a map entry and
 777          * the two entries represent the same mapping.  More formally, two
 778          * entries {@code e1} and {@code e2} represent the same mapping
 779          * if<pre>
 780          *   (e1.getKey()==null ?
 781          *    e2.getKey()==null :
 782          *    e1.getKey().equals(e2.getKey()))
 783          *   &amp;&amp;
 784          *   (e1.getValue()==null ?
 785          *    e2.getValue()==null :
 786          *    e1.getValue().equals(e2.getValue()))</pre>
 787          * This ensures that the {@code equals} method works properly across
 788          * different implementations of the {@code Map.Entry} interface.
 789          *
 790          * @param o object to be compared for equality with this map entry
 791          * @return {@code true} if the specified object is equal to this map
 792          *         entry
 793          * @see    #hashCode
 794          */
 795         public boolean equals(Object o) {
 796             if (!(o instanceof Map.Entry))
 797                 return false;
 798             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
 799             return eq(key, e.getKey()) && eq(value, e.getValue());
 800         }
 801 
 802         /**
 803          * Returns the hash code value for this map entry.  The hash code
 804          * of a map entry {@code e} is defined to be: <pre>
 805          *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
 806          *   (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
 807          * This ensures that {@code e1.equals(e2)} implies that
 808          * {@code e1.hashCode()==e2.hashCode()} for any two Entries
 809          * {@code e1} and {@code e2}, as required by the general
 810          * contract of {@link Object#hashCode}.
 811          *
 812          * @return the hash code value for this map entry
 813          * @see    #equals
 814          */
 815         public int hashCode() {
 816             return (key   == null ? 0 :   key.hashCode()) ^
 817                    (value == null ? 0 : value.hashCode());
 818         }
 819 
 820         /**
 821          * Returns a String representation of this map entry.  This
 822          * implementation returns the string representation of this
 823          * entry's key followed by the equals character ("<tt>=</tt>")
 824          * followed by the string representation of this entry's value.
 825          *
 826          * @return a String representation of this map entry
 827          */
 828         public String toString() {
 829             return key + "=" + value;
 830         }
 831 
 832     }
 833 
 834 }