1 /* 2 * Copyright (c) 1997, 2010, 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<K,V> m = (Map<K,V>) 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<K,V> result = (AbstractMap<K,V>)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 * && 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 * && 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 }