001 /* 002 * @(#)AbstractMap.java 1.32 01/12/03 003 * 004 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 005 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 006 */ 007 008 package org.objectweb.jac.util; 009 010 import java.util.AbstractSet; 011 import java.util.Collection; 012 import java.util.Iterator; 013 import java.util.Map.Entry; 014 import java.util.Map; 015 import java.util.Set; 016 import java.util.AbstractCollection; 017 018 /** 019 * This class provides a skeletal implementation of the <tt>Map</tt> 020 * interface, to minimize the effort required to implement this interface. <p> 021 * 022 * To implement an unmodifiable map, the programmer needs only to extend this 023 * class and provide an implementation for the <tt>entrySet</tt> method, which 024 * returns a set-view of the map's mappings. Typically, the returned set 025 * will, in turn, be implemented atop <tt>AbstractSet</tt>. This set should 026 * not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator 027 * should not support the <tt>remove</tt> method.<p> 028 * 029 * To implement a modifiable map, the programmer must additionally override 030 * this class's <tt>put</tt> method (which otherwise throws an 031 * <tt>UnsupportedOperationException</tt>), and the iterator returned by 032 * <tt>entrySet().iterator()</tt> must additionally implement its 033 * <tt>remove</tt> method.<p> 034 * 035 * The programmer should generally provide a void (no argument) and map 036 * constructor, as per the recommendation in the <tt>Map</tt> interface 037 * specification.<p> 038 * 039 * The documentation for each non-abstract methods in this class describes its 040 * implementation in detail. Each of these methods may be overridden if the 041 * map being implemented admits a more efficient implementation. 042 * 043 * @author Josh Bloch 044 * @version 1.32, 12/03/01 045 * @see Map 046 * @see Collection 047 * @since 1.2 048 */ 049 050 public abstract class AbstractMap implements Map { 051 /** 052 * Sole constructor. (For invocation by subclass constructors, typically 053 * implicit.) 054 */ 055 protected AbstractMap() { 056 } 057 058 // Query Operations 059 060 /** 061 * Returns the number of key-value mappings in this map. If the map 062 * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns 063 * <tt>Integer.MAX_VALUE</tt>.<p> 064 * 065 * This implementation returns <tt>entrySet().size()</tt>. 066 * 067 * @return the number of key-value mappings in this map. 068 */ 069 public int size() { 070 return entrySet().size(); 071 } 072 073 /** 074 * Returns <tt>true</tt> if this map contains no key-value mappings. <p> 075 * 076 * This implementation returns <tt>size() == 0</tt>. 077 * 078 * @return <tt>true</tt> if this map contains no key-value mappings. 079 */ 080 public boolean isEmpty() { 081 return size() == 0; 082 } 083 084 /** 085 * Returns <tt>true</tt> if this map maps one or more keys to this value. 086 * More formally, returns <tt>true</tt> if and only if this map contains 087 * at least one mapping to a value <tt>v</tt> such that <tt>(value==null ? 088 * v==null : value.equals(v))</tt>. This operation will probably require 089 * time linear in the map size for most implementations of map.<p> 090 * 091 * This implementation iterates over entrySet() searching for an entry 092 * with the specified value. If such an entry is found, <tt>true</tt> is 093 * returned. If the iteration terminates without finding such an entry, 094 * <tt>false</tt> is returned. Note that this implementation requires 095 * linear time in the size of the map. 096 * 097 * @param value value whose presence in this map is to be tested. 098 * 099 * @return <tt>true</tt> if this map maps one or more keys to this value. 100 */ 101 public boolean containsValue(Object value) { 102 Iterator i = entrySet().iterator(); 103 if (value==null) { 104 while (i.hasNext()) { 105 Entry e = (Entry) i.next(); 106 if (e.getValue()==null) 107 return true; 108 } 109 } else { 110 while (i.hasNext()) { 111 Entry e = (Entry) i.next(); 112 if (value.equals(e.getValue())) 113 return true; 114 } 115 } 116 return false; 117 } 118 119 /** 120 * Returns <tt>true</tt> if this map contains a mapping for the specified 121 * key. <p> 122 * 123 * This implementation iterates over <tt>entrySet()</tt> searching for an 124 * entry with the specified key. If such an entry is found, <tt>true</tt> 125 * is returned. If the iteration terminates without finding such an 126 * entry, <tt>false</tt> is returned. Note that this implementation 127 * requires linear time in the size of the map; many implementations will 128 * override this method. 129 * 130 * @param key key whose presence in this map is to be tested. 131 * @return <tt>true</tt> if this map contains a mapping for the specified 132 * key. 133 * 134 * @throws NullPointerException key is <tt>null</tt> and this map does not 135 * not permit <tt>null</tt> keys. 136 */ 137 public boolean containsKey(Object key) { 138 Iterator i = entrySet().iterator(); 139 if (key==null) { 140 while (i.hasNext()) { 141 Entry e = (Entry) i.next(); 142 if (e.getKey()==null) 143 return true; 144 } 145 } else { 146 while (i.hasNext()) { 147 Entry e = (Entry) i.next(); 148 if (key.equals(e.getKey())) 149 return true; 150 } 151 } 152 return false; 153 } 154 155 /** 156 * Returns the value to which this map maps the specified key. Returns 157 * <tt>null</tt> if the map contains no mapping for this key. A return 158 * value of <tt>null</tt> does not <i>necessarily</i> indicate that the 159 * map contains no mapping for the key; it's also possible that the map 160 * explicitly maps the key to <tt>null</tt>. The containsKey operation 161 * may be used to distinguish these two cases. <p> 162 * 163 * This implementation iterates over <tt>entrySet()</tt> searching for an 164 * entry with the specified key. If such an entry is found, the entry's 165 * value is returned. If the iteration terminates without finding such an 166 * entry, <tt>null</tt> is returned. Note that this implementation 167 * requires linear time in the size of the map; many implementations will 168 * override this method. 169 * 170 * @param key key whose associated value is to be returned. 171 * @return the value to which this map maps the specified key. 172 * 173 * @throws NullPointerException if the key is <tt>null</tt> and this map 174 * does not not permit <tt>null</tt> keys. 175 * 176 * @see #containsKey(Object) 177 */ 178 public Object get(Object key) { 179 Iterator i = entrySet().iterator(); 180 if (key==null) { 181 while (i.hasNext()) { 182 Entry e = (Entry) i.next(); 183 if (e.getKey()==null) 184 return e.getValue(); 185 } 186 } else { 187 while (i.hasNext()) { 188 Entry e = (Entry) i.next(); 189 if (key.equals(e.getKey())) 190 return e.getValue(); 191 } 192 } 193 return null; 194 } 195 196 197 // Modification Operations 198 199 /** 200 * Associates the specified value with the specified key in this map 201 * (optional operation). If the map previously contained a mapping for 202 * this key, the old value is replaced.<p> 203 * 204 * This implementation always throws an 205 * <tt>UnsupportedOperationException</tt>. 206 * 207 * @param key key with which the specified value is to be associated. 208 * @param value value to be associated with the specified key. 209 * 210 * @return previous value associated with specified key, or <tt>null</tt> 211 * if there was no mapping for key. (A <tt>null</tt> return can 212 * also indicate that the map previously associated <tt>null</tt> 213 * with the specified key, if the implementation supports 214 * <tt>null</tt> values.) 215 * 216 * @throws UnsupportedOperationException if the <tt>put</tt> operation is 217 * not supported by this map. 218 * 219 * @throws ClassCastException if the class of the specified key or value 220 * prevents it from being stored in this map. 221 * 222 * @throws IllegalArgumentException if some aspect of this key or value * 223 * prevents it from being stored in this map. 224 * 225 * @throws NullPointerException this map does not permit <tt>null</tt> 226 * keys or values, and the specified key or value is 227 * <tt>null</tt>. 228 */ 229 public Object put(Object key, Object value) { 230 throw new UnsupportedOperationException(); 231 } 232 233 /** 234 * Removes the mapping for this key from this map if present (optional 235 * operation). <p> 236 * 237 * This implementation iterates over <tt>entrySet()</tt> searching for an 238 * entry with the specified key. If such an entry is found, its value is 239 * obtained with its <tt>getValue</tt> operation, the entry is is removed 240 * from the Collection (and the backing map) with the iterator's 241 * <tt>remove</tt> operation, and the saved value is returned. If the 242 * iteration terminates without finding such an entry, <tt>null</tt> is 243 * returned. Note that this implementation requires linear time in the 244 * size of the map; many implementations will override this method.<p> 245 * 246 * Note that this implementation throws an 247 * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt> iterator 248 * does not support the <tt>remove</tt> method and this map contains a 249 * mapping for the specified key. 250 * 251 * @param key key whose mapping is to be removed from the map. 252 * @return previous value associated with specified key, or <tt>null</tt> 253 * if there was no entry for key. (A <tt>null</tt> return can 254 * also indicate that the map previously associated <tt>null</tt> 255 * with the specified key, if the implementation supports 256 * <tt>null</tt> values.) 257 * @throws UnsupportedOperationException if the <tt>remove</tt> operation 258 * is not supported by this map. 259 */ 260 public Object remove(Object key) { 261 Iterator i = entrySet().iterator(); 262 Entry correctEntry = null; 263 if (key==null) { 264 while (correctEntry==null && i.hasNext()) { 265 Entry e = (Entry) i.next(); 266 if (e.getKey()==null) 267 correctEntry = e; 268 } 269 } else { 270 while (correctEntry==null && i.hasNext()) { 271 Entry e = (Entry) i.next(); 272 if (key.equals(e.getKey())) 273 correctEntry = e; 274 } 275 } 276 277 Object oldValue = null; 278 if (correctEntry !=null) { 279 oldValue = correctEntry.getValue(); 280 i.remove(); 281 } 282 return oldValue; 283 } 284 285 286 // Bulk Operations 287 288 /** 289 * Copies all of the mappings from the specified map to this map 290 * (optional operation). These mappings will replace any mappings that 291 * this map had for any of the keys currently in the specified map.<p> 292 * 293 * This implementation iterates over the specified map's 294 * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt> 295 * operation once for each entry returned by the iteration.<p> 296 * 297 * Note that this implementation throws an 298 * <tt>UnsupportedOperationException</tt> if this map does not support 299 * the <tt>put</tt> operation and the specified map is nonempty. 300 * 301 * @param t mappings to be stored in this map. 302 * 303 * @throws UnsupportedOperationException if the <tt>putAll</tt> operation 304 * is not supported by this map. 305 * 306 * @throws ClassCastException if the class of a key or value in the 307 * specified map prevents it from being stored in this map. 308 * 309 * @throws IllegalArgumentException if some aspect of a key or value in 310 * the specified map prevents it from being stored in this map. 311 * @throws NullPointerException the specified map is <tt>null</tt>, or if 312 * this map does not permit <tt>null</tt> keys or values, and the 313 * specified map contains <tt>null</tt> keys or values. 314 */ 315 public void putAll(Map t) { 316 Iterator i = t.entrySet().iterator(); 317 while (i.hasNext()) { 318 Entry e = (Entry) i.next(); 319 put(e.getKey(), e.getValue()); 320 } 321 } 322 323 /** 324 * Removes all mappings from this map (optional operation). <p> 325 * 326 * This implementation calls <tt>entrySet().clear()</tt>. 327 * 328 * Note that this implementation throws an 329 * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt> 330 * does not support the <tt>clear</tt> operation. 331 * 332 * @throws UnsupportedOperationException clear is not supported 333 * by this map. 334 */ 335 public void clear() { 336 entrySet().clear(); 337 } 338 339 340 // Views 341 342 /** 343 * Each of these fields are initialized to contain an instance of the 344 * appropriate view the first time this view is requested. The views are 345 * stateless, so there's no reason to create more than one of each. 346 */ 347 transient volatile Set keySet = null; 348 transient volatile Collection values = null; 349 350 /** 351 * Returns a Set view of the keys contained in this map. The Set is 352 * backed by the map, so changes to the map are reflected in the Set, 353 * and vice-versa. (If the map is modified while an iteration over 354 * the Set is in progress, the results of the iteration are undefined.) 355 * The Set supports element removal, which removes the corresponding entry 356 * from the map, via the Iterator.remove, Set.remove, removeAll 357 * retainAll, and clear operations. It does not support the add or 358 * addAll operations.<p> 359 * 360 * This implementation returns a Set that subclasses 361 * AbstractSet. The subclass's iterator method returns a "wrapper 362 * object" over this map's entrySet() iterator. The size method delegates 363 * to this map's size method and the contains method delegates to this 364 * map's containsKey method.<p> 365 * 366 * The Set is created the first time this method is called, 367 * and returned in response to all subsequent calls. No synchronization 368 * is performed, so there is a slight chance that multiple calls to this 369 * method will not all return the same Set. 370 * 371 * @return a Set view of the keys contained in this map. 372 */ 373 public Set keySet() { 374 if (keySet == null) { 375 keySet = new AbstractSet() { 376 public Iterator iterator() { 377 return new Iterator() { 378 private Iterator i = entrySet().iterator(); 379 380 public boolean hasNext() { 381 return i.hasNext(); 382 } 383 384 public Object next() { 385 return ((Entry)i.next()).getKey(); 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 contains(Object k) { 399 return AbstractMap.this.containsKey(k); 400 } 401 }; 402 } 403 return keySet; 404 } 405 406 /** 407 * Returns a collection view of the values contained in this map. The 408 * collection is backed by the map, so changes to the map are reflected in 409 * the collection, and vice-versa. (If the map is modified while an 410 * iteration over the collection is in progress, the results of the 411 * iteration are undefined.) The collection supports element removal, 412 * which removes the corresponding entry from the map, via the 413 * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, 414 * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> operations. 415 * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.<p> 416 * 417 * This implementation returns a collection that subclasses abstract 418 * collection. The subclass's iterator method returns a "wrapper object" 419 * over this map's <tt>entrySet()</tt> iterator. The size method 420 * delegates to this map's size method and the contains method delegates 421 * to this map's containsValue method.<p> 422 * 423 * The collection is created the first time this method is called, and 424 * returned in response to all subsequent calls. No synchronization is 425 * performed, so there is a slight chance that multiple calls to this 426 * method will not all return the same Collection. 427 * 428 * @return a collection view of the values contained in this map. 429 */ 430 public Collection values() { 431 if (values == null) { 432 values = new AbstractCollection() { 433 public Iterator iterator() { 434 return new Iterator() { 435 private Iterator i = entrySet().iterator(); 436 437 public boolean hasNext() { 438 return i.hasNext(); 439 } 440 441 public Object next() { 442 return ((Entry)i.next()).getValue(); 443 } 444 445 public void remove() { 446 i.remove(); 447 } 448 }; 449 } 450 451 public int size() { 452 return AbstractMap.this.size(); 453 } 454 455 public boolean contains(Object v) { 456 return AbstractMap.this.containsValue(v); 457 } 458 }; 459 } 460 return values; 461 } 462 463 /** 464 * Returns a set view of the mappings contained in this map. Each element 465 * in this set is a Map.Entry. The set is backed by the map, so changes 466 * to the map are reflected in the set, and vice-versa. (If the map is 467 * modified while an iteration over the set is in progress, the results of 468 * the iteration are undefined.) The set supports element removal, which 469 * removes the corresponding entry from the map, via the 470 * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>, 471 * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not support 472 * the <tt>add</tt> or <tt>addAll</tt> operations. 473 * 474 * @return a set view of the mappings contained in this map. 475 */ 476 public abstract Set entrySet(); 477 478 479 // Comparison and hashing 480 481 /** 482 * Compares the specified object with this map for equality. Returns 483 * <tt>true</tt> if the given object is also a map and the two maps 484 * represent the same mappings. More formally, two maps <tt>t1</tt> and 485 * <tt>t2</tt> represent the same mappings if 486 * <tt>t1.keySet().equals(t2.keySet())</tt> and for every key <tt>k</tt> 487 * in <tt>t1.keySet()</tt>, <tt> (t1.get(k)==null ? t2.get(k)==null : 488 * t1.get(k).equals(t2.get(k))) </tt>. This ensures that the 489 * <tt>equals</tt> method works properly across different implementations 490 * of the map interface.<p> 491 * 492 * This implementation first checks if the specified object is this map; 493 * if so it returns <tt>true</tt>. Then, it checks if the specified 494 * object is a map whose size is identical to the size of this set; if 495 * not, it it returns <tt>false</tt>. If so, it iterates over this map's 496 * <tt>entrySet</tt> collection, and checks that the specified map 497 * contains each mapping that this map contains. If the specified map 498 * fails to contain such a mapping, <tt>false</tt> is returned. If the 499 * iteration completes, <tt>true</tt> is returned. 500 * 501 * @param o object to be compared for equality with this map. 502 * @return <tt>true</tt> if the specified object is equal to this map. 503 */ 504 public boolean equals(Object o) { 505 if (o == this) 506 return true; 507 508 if (!(o instanceof Map)) 509 return false; 510 Map t = (Map) o; 511 if (t.size() != size()) 512 return false; 513 514 try { 515 Iterator i = entrySet().iterator(); 516 while (i.hasNext()) { 517 Entry e = (Entry) i.next(); 518 Object key = e.getKey(); 519 Object value = e.getValue(); 520 if (value == null) { 521 if (!(t.get(key)==null && t.containsKey(key))) 522 return false; 523 } else { 524 if (!value.equals(t.get(key))) 525 return false; 526 } 527 } 528 } catch(ClassCastException unused) { 529 return false; 530 } catch(NullPointerException unused) { 531 return false; 532 } 533 534 return true; 535 } 536 537 /** 538 * Returns the hash code value for this map. The hash code of a map is 539 * defined to be the sum of the hash codes of each entry in the map's 540 * <tt>entrySet()</tt> view. This ensures that <tt>t1.equals(t2)</tt> 541 * implies that <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps 542 * <tt>t1</tt> and <tt>t2</tt>, as required by the general contract of 543 * Object.hashCode.<p> 544 * 545 * This implementation iterates over <tt>entrySet()</tt>, calling 546 * <tt>hashCode</tt> on each element (entry) in the Collection, and adding 547 * up the results. 548 * 549 * @return the hash code value for this map. 550 * @see java.util.Map.Entry#hashCode() 551 * @see Object#hashCode() 552 * @see Object#equals(Object) 553 * @see Set#equals(Object) 554 */ 555 public int hashCode() { 556 int h = 0; 557 Iterator i = entrySet().iterator(); 558 while (i.hasNext()) 559 h += i.next().hashCode(); 560 return h; 561 } 562 563 /** 564 * Returns a string representation of this map. The string representation 565 * consists of a list of key-value mappings in the order returned by the 566 * map's <tt>entrySet</tt> view's iterator, enclosed in braces 567 * (<tt>"{}"</tt>). Adjacent mappings are separated by the characters 568 * <tt>", "</tt> (comma and space). Each key-value mapping is rendered as 569 * the key followed by an equals sign (<tt>"="</tt>) followed by the 570 * associated value. Keys and values are converted to strings as by 571 * <tt>String.valueOf(Object)</tt>.<p> 572 * 573 * This implementation creates an empty string buffer, appends a left 574 * brace, and iterates over the map's <tt>entrySet</tt> view, appending 575 * the string representation of each <tt>map.entry</tt> in turn. After 576 * appending each entry except the last, the string <tt>", "</tt> is 577 * appended. Finally a right brace is appended. A string is obtained 578 * from the stringbuffer, and returned. 579 * 580 * @return a String representation of this map. 581 */ 582 public String toString() { 583 StringBuffer buf = new StringBuffer(); 584 buf.append("{"); 585 586 Iterator i = entrySet().iterator(); 587 boolean hasNext = i.hasNext(); 588 while (hasNext) { 589 Entry e = (Entry) (i.next()); 590 Object key = e.getKey(); 591 Object value = e.getValue(); 592 buf.append((key == this ? "(this Map)" : key) + "=" + 593 (value == this ? "(this Map)": value)); 594 595 hasNext = i.hasNext(); 596 if (hasNext) 597 buf.append(", "); 598 } 599 600 buf.append("}"); 601 return buf.toString(); 602 } 603 604 /** 605 * Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys 606 * and values themselves are not cloned. 607 * 608 * @return a shallow copy of this map. 609 */ 610 protected Object clone() throws CloneNotSupportedException { 611 AbstractMap result = (AbstractMap)super.clone(); 612 result.keySet = null; 613 result.values = null; 614 return result; 615 } 616 617 /** 618 * This should be made public as soon as possible. It greately simplifies 619 * the task of implementing Map. 620 */ 621 static class SimpleEntry implements Entry { 622 Object key; 623 Object value; 624 625 public SimpleEntry(Object key, Object value) { 626 this.key = key; 627 this.value = value; 628 } 629 630 public SimpleEntry(Map.Entry e) { 631 this.key = e.getKey(); 632 this.value = e.getValue(); 633 } 634 635 public Object getKey() { 636 return key; 637 } 638 639 public Object getValue() { 640 return value; 641 } 642 643 public Object setValue(Object value) { 644 Object oldValue = this.value; 645 this.value = value; 646 return oldValue; 647 } 648 649 public boolean equals(Object o) { 650 if (!(o instanceof Map.Entry)) 651 return false; 652 Map.Entry e = (Map.Entry)o; 653 return eq(key, e.getKey()) && eq(value, e.getValue()); 654 } 655 656 public int hashCode() { 657 Object v; 658 return ((key == null) ? 0 : key.hashCode()) ^ 659 ((value == null) ? 0 : value.hashCode()); 660 } 661 662 public String toString() { 663 return key + "=" + value; 664 } 665 666 private static boolean eq(Object o1, Object o2) { 667 return (o1 == null ? o2 == null : o1.equals(o2)); 668 } 669 } 670 }