1. /*
  2. * @(#)AbstractMap.java 1.42 04/02/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util;
  8. import java.util.Map.Entry;
  9. /**
  10. * This class provides a skeletal implementation of the <tt>Map</tt>
  11. * interface, to minimize the effort required to implement this interface. <p>
  12. *
  13. * To implement an unmodifiable map, the programmer needs only to extend this
  14. * class and provide an implementation for the <tt>entrySet</tt> method, which
  15. * returns a set-view of the map's mappings. Typically, the returned set
  16. * will, in turn, be implemented atop <tt>AbstractSet</tt>. This set should
  17. * not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator
  18. * should not support the <tt>remove</tt> method.<p>
  19. *
  20. * To implement a modifiable map, the programmer must additionally override
  21. * this class's <tt>put</tt> method (which otherwise throws an
  22. * <tt>UnsupportedOperationException</tt>), and the iterator returned by
  23. * <tt>entrySet().iterator()</tt> must additionally implement its
  24. * <tt>remove</tt> method.<p>
  25. *
  26. * The programmer should generally provide a void (no argument) and map
  27. * constructor, as per the recommendation in the <tt>Map</tt> interface
  28. * specification.<p>
  29. *
  30. * The documentation for each non-abstract methods in this class describes its
  31. * implementation in detail. Each of these methods may be overridden if the
  32. * map being implemented admits a more efficient implementation.<p>
  33. *
  34. * This class is a member of the
  35. * <a href="{@docRoot}/../guide/collections/index.html">
  36. * Java Collections Framework</a>.
  37. *
  38. * @author Josh Bloch
  39. * @author Neal Gafter
  40. * @version 1.42, 02/19/04
  41. * @see Map
  42. * @see Collection
  43. * @since 1.2
  44. */
  45. public abstract class AbstractMap<K,V> implements Map<K,V> {
  46. /**
  47. * Sole constructor. (For invocation by subclass constructors, typically
  48. * implicit.)
  49. */
  50. protected AbstractMap() {
  51. }
  52. // Query Operations
  53. /**
  54. * Returns the number of key-value mappings in this map. If the map
  55. * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
  56. * <tt>Integer.MAX_VALUE</tt>.<p>
  57. *
  58. * This implementation returns <tt>entrySet().size()</tt>.
  59. *
  60. * @return the number of key-value mappings in this map.
  61. */
  62. public int size() {
  63. return entrySet().size();
  64. }
  65. /**
  66. * Returns <tt>true</tt> if this map contains no key-value mappings. <p>
  67. *
  68. * This implementation returns <tt>size() == 0</tt>.
  69. *
  70. * @return <tt>true</tt> if this map contains no key-value mappings.
  71. */
  72. public boolean isEmpty() {
  73. return size() == 0;
  74. }
  75. /**
  76. * Returns <tt>true</tt> if this map maps one or more keys to this value.
  77. * More formally, returns <tt>true</tt> if and only if this map contains
  78. * at least one mapping to a value <tt>v</tt> such that <tt>(value==null ?
  79. * v==null : value.equals(v))</tt>. This operation will probably require
  80. * time linear in the map size for most implementations of map.<p>
  81. *
  82. * This implementation iterates over entrySet() searching for an entry
  83. * with the specified value. If such an entry is found, <tt>true</tt> is
  84. * returned. If the iteration terminates without finding such an entry,
  85. * <tt>false</tt> is returned. Note that this implementation requires
  86. * linear time in the size of the map.
  87. *
  88. * @param value value whose presence in this map is to be tested.
  89. *
  90. * @return <tt>true</tt> if this map maps one or more keys to this value.
  91. */
  92. public boolean containsValue(Object value) {
  93. Iterator<Entry<K,V>> i = entrySet().iterator();
  94. if (value==null) {
  95. while (i.hasNext()) {
  96. Entry<K,V> e = i.next();
  97. if (e.getValue()==null)
  98. return true;
  99. }
  100. } else {
  101. while (i.hasNext()) {
  102. Entry<K,V> e = i.next();
  103. if (value.equals(e.getValue()))
  104. return true;
  105. }
  106. }
  107. return false;
  108. }
  109. /**
  110. * Returns <tt>true</tt> if this map contains a mapping for the specified
  111. * key. <p>
  112. *
  113. * This implementation iterates over <tt>entrySet()</tt> searching for an
  114. * entry with the specified key. If such an entry is found, <tt>true</tt>
  115. * is returned. If the iteration terminates without finding such an
  116. * entry, <tt>false</tt> is returned. Note that this implementation
  117. * requires linear time in the size of the map; many implementations will
  118. * override this method.
  119. *
  120. * @param key key whose presence in this map is to be tested.
  121. * @return <tt>true</tt> if this map contains a mapping for the specified
  122. * key.
  123. *
  124. * @throws NullPointerException if the key is <tt>null</tt> and this map
  125. * does not permit <tt>null</tt> keys.
  126. */
  127. public boolean containsKey(Object key) {
  128. Iterator<Map.Entry<K,V>> i = entrySet().iterator();
  129. if (key==null) {
  130. while (i.hasNext()) {
  131. Entry<K,V> e = i.next();
  132. if (e.getKey()==null)
  133. return true;
  134. }
  135. } else {
  136. while (i.hasNext()) {
  137. Entry<K,V> e = i.next();
  138. if (key.equals(e.getKey()))
  139. return true;
  140. }
  141. }
  142. return false;
  143. }
  144. /**
  145. * Returns the value to which this map maps the specified key. Returns
  146. * <tt>null</tt> if the map contains no mapping for this key. A return
  147. * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
  148. * map contains no mapping for the key; it's also possible that the map
  149. * explicitly maps the key to <tt>null</tt>. The containsKey operation
  150. * may be used to distinguish these two cases. <p>
  151. *
  152. * This implementation iterates over <tt>entrySet()</tt> searching for an
  153. * entry with the specified key. If such an entry is found, the entry's
  154. * value is returned. If the iteration terminates without finding such an
  155. * entry, <tt>null</tt> is returned. Note that this implementation
  156. * requires linear time in the size of the map; many implementations will
  157. * override this method.
  158. *
  159. * @param key key whose associated value is to be returned.
  160. * @return the value to which this map maps the specified key.
  161. *
  162. * @throws NullPointerException if the key is <tt>null</tt> and this map
  163. * does not permit <tt>null</tt> keys.
  164. *
  165. * @see #containsKey(Object)
  166. */
  167. public V get(Object key) {
  168. Iterator<Entry<K,V>> i = entrySet().iterator();
  169. if (key==null) {
  170. while (i.hasNext()) {
  171. Entry<K,V> e = i.next();
  172. if (e.getKey()==null)
  173. return e.getValue();
  174. }
  175. } else {
  176. while (i.hasNext()) {
  177. Entry<K,V> e = i.next();
  178. if (key.equals(e.getKey()))
  179. return e.getValue();
  180. }
  181. }
  182. return null;
  183. }
  184. // Modification Operations
  185. /**
  186. * Associates the specified value with the specified key in this map
  187. * (optional operation). If the map previously contained a mapping for
  188. * this key, the old value is replaced.<p>
  189. *
  190. * This implementation always throws an
  191. * <tt>UnsupportedOperationException</tt>.
  192. *
  193. * @param key key with which the specified value is to be associated.
  194. * @param value value to be associated with the specified key.
  195. *
  196. * @return previous value associated with specified key, or <tt>null</tt>
  197. * if there was no mapping for key. (A <tt>null</tt> return can
  198. * also indicate that the map previously associated <tt>null</tt>
  199. * with the specified key, if the implementation supports
  200. * <tt>null</tt> values.)
  201. *
  202. * @throws UnsupportedOperationException if the <tt>put</tt> operation is
  203. * not supported by this map.
  204. *
  205. * @throws ClassCastException if the class of the specified key or value
  206. * prevents it from being stored in this map.
  207. *
  208. * @throws IllegalArgumentException if some aspect of this key or value *
  209. * prevents it from being stored in this map.
  210. *
  211. * @throws NullPointerException if this map does not permit <tt>null</tt>
  212. * keys or values, and the specified key or value is
  213. * <tt>null</tt>.
  214. */
  215. public V put(K key, V value) {
  216. throw new UnsupportedOperationException();
  217. }
  218. /**
  219. * Removes the mapping for this key from this map if present (optional
  220. * operation). <p>
  221. *
  222. * This implementation iterates over <tt>entrySet()</tt> searching for an
  223. * entry with the specified key. If such an entry is found, its value is
  224. * obtained with its <tt>getValue</tt> operation, the entry is removed
  225. * from the Collection (and the backing map) with the iterator's
  226. * <tt>remove</tt> operation, and the saved value is returned. If the
  227. * iteration terminates without finding such an entry, <tt>null</tt> is
  228. * returned. Note that this implementation requires linear time in the
  229. * size of the map; many implementations will override this method.<p>
  230. *
  231. * Note that this implementation throws an
  232. * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt> iterator
  233. * does not support the <tt>remove</tt> method and this map contains a
  234. * mapping for the specified key.
  235. *
  236. * @param key key whose mapping is to be removed from the map.
  237. * @return previous value associated with specified key, or <tt>null</tt>
  238. * if there was no entry for key. (A <tt>null</tt> return can
  239. * also indicate that the map previously associated <tt>null</tt>
  240. * with the specified key, if the implementation supports
  241. * <tt>null</tt> values.)
  242. * @throws UnsupportedOperationException if the <tt>remove</tt> operation
  243. * is not supported by this map.
  244. */
  245. public V remove(Object key) {
  246. Iterator<Entry<K,V>> i = entrySet().iterator();
  247. Entry<K,V> correctEntry = null;
  248. if (key==null) {
  249. while (correctEntry==null && i.hasNext()) {
  250. Entry<K,V> e = i.next();
  251. if (e.getKey()==null)
  252. correctEntry = e;
  253. }
  254. } else {
  255. while (correctEntry==null && i.hasNext()) {
  256. Entry<K,V> e = i.next();
  257. if (key.equals(e.getKey()))
  258. correctEntry = e;
  259. }
  260. }
  261. V oldValue = null;
  262. if (correctEntry !=null) {
  263. oldValue = correctEntry.getValue();
  264. i.remove();
  265. }
  266. return oldValue;
  267. }
  268. // Bulk Operations
  269. /**
  270. * Copies all of the mappings from the specified map to this map
  271. * (optional operation). These mappings will replace any mappings that
  272. * this map had for any of the keys currently in the specified map.<p>
  273. *
  274. * This implementation iterates over the specified map's
  275. * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
  276. * operation once for each entry returned by the iteration.<p>
  277. *
  278. * Note that this implementation throws an
  279. * <tt>UnsupportedOperationException</tt> if this map does not support
  280. * the <tt>put</tt> operation and the specified map is nonempty.
  281. *
  282. * @param t mappings to be stored in this map.
  283. *
  284. * @throws UnsupportedOperationException if the <tt>putAll</tt> operation
  285. * is not supported by this map.
  286. *
  287. * @throws ClassCastException if the class of a key or value in the
  288. * specified map prevents it from being stored in this map.
  289. *
  290. * @throws IllegalArgumentException if some aspect of a key or value in
  291. * the specified map prevents it from being stored in this map.
  292. * @throws NullPointerException if the specified map is <tt>null</tt>, or if
  293. * this map does not permit <tt>null</tt> keys or values, and the
  294. * specified map contains <tt>null</tt> keys or values.
  295. */
  296. public void putAll(Map<? extends K, ? extends V> t) {
  297. Iterator<? extends Entry<? extends K, ? extends V>> i = t.entrySet().iterator();
  298. while (i.hasNext()) {
  299. Entry<? extends K, ? extends V> e = i.next();
  300. put(e.getKey(), e.getValue());
  301. }
  302. }
  303. /**
  304. * Removes all mappings from this map (optional operation). <p>
  305. *
  306. * This implementation calls <tt>entrySet().clear()</tt>.
  307. *
  308. * Note that this implementation throws an
  309. * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
  310. * does not support the <tt>clear</tt> operation.
  311. *
  312. * @throws UnsupportedOperationException clear is not supported
  313. * by this map.
  314. */
  315. public void clear() {
  316. entrySet().clear();
  317. }
  318. // Views
  319. /**
  320. * Each of these fields are initialized to contain an instance of the
  321. * appropriate view the first time this view is requested. The views are
  322. * stateless, so there's no reason to create more than one of each.
  323. */
  324. transient volatile Set<K> keySet = null;
  325. transient volatile Collection<V> values = null;
  326. /**
  327. * Returns a Set view of the keys contained in this map. The Set is
  328. * backed by the map, so changes to the map are reflected in the Set,
  329. * and vice-versa. (If the map is modified while an iteration over
  330. * the Set is in progress, the results of the iteration are undefined.)
  331. * The Set supports element removal, which removes the corresponding entry
  332. * from the map, via the Iterator.remove, Set.remove, removeAll
  333. * retainAll, and clear operations. It does not support the add or
  334. * addAll operations.<p>
  335. *
  336. * This implementation returns a Set that subclasses
  337. * AbstractSet. The subclass's iterator method returns a "wrapper
  338. * object" over this map's entrySet() iterator. The size method delegates
  339. * to this map's size method and the contains method delegates to this
  340. * map's containsKey method.<p>
  341. *
  342. * The Set is created the first time this method is called,
  343. * and returned in response to all subsequent calls. No synchronization
  344. * is performed, so there is a slight chance that multiple calls to this
  345. * method will not all return the same Set.
  346. *
  347. * @return a Set view of the keys contained in this map.
  348. */
  349. public Set<K> keySet() {
  350. if (keySet == null) {
  351. keySet = new AbstractSet<K>() {
  352. public Iterator<K> iterator() {
  353. return new Iterator<K>() {
  354. private Iterator<Entry<K,V>> i = entrySet().iterator();
  355. public boolean hasNext() {
  356. return i.hasNext();
  357. }
  358. public K next() {
  359. return i.next().getKey();
  360. }
  361. public void remove() {
  362. i.remove();
  363. }
  364. };
  365. }
  366. public int size() {
  367. return AbstractMap.this.size();
  368. }
  369. public boolean contains(Object k) {
  370. return AbstractMap.this.containsKey(k);
  371. }
  372. };
  373. }
  374. return keySet;
  375. }
  376. /**
  377. * Returns a collection view of the values contained in this map. The
  378. * collection is backed by the map, so changes to the map are reflected in
  379. * the collection, and vice-versa. (If the map is modified while an
  380. * iteration over the collection is in progress, the results of the
  381. * iteration are undefined.) The collection supports element removal,
  382. * which removes the corresponding entry from the map, via the
  383. * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
  384. * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> operations.
  385. * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.<p>
  386. *
  387. * This implementation returns a collection that subclasses abstract
  388. * collection. The subclass's iterator method returns a "wrapper object"
  389. * over this map's <tt>entrySet()</tt> iterator. The size method
  390. * delegates to this map's size method and the contains method delegates
  391. * to this map's containsValue method.<p>
  392. *
  393. * The collection is created the first time this method is called, and
  394. * returned in response to all subsequent calls. No synchronization is
  395. * performed, so there is a slight chance that multiple calls to this
  396. * method will not all return the same Collection.
  397. *
  398. * @return a collection view of the values contained in this map.
  399. */
  400. public Collection<V> values() {
  401. if (values == null) {
  402. values = new AbstractCollection<V>() {
  403. public Iterator<V> iterator() {
  404. return new Iterator<V>() {
  405. private Iterator<Entry<K,V>> i = entrySet().iterator();
  406. public boolean hasNext() {
  407. return i.hasNext();
  408. }
  409. public V next() {
  410. return i.next().getValue();
  411. }
  412. public void remove() {
  413. i.remove();
  414. }
  415. };
  416. }
  417. public int size() {
  418. return AbstractMap.this.size();
  419. }
  420. public boolean contains(Object v) {
  421. return AbstractMap.this.containsValue(v);
  422. }
  423. };
  424. }
  425. return values;
  426. }
  427. /**
  428. * Returns a set view of the mappings contained in this map. Each element
  429. * in this set is a Map.Entry. The set is backed by the map, so changes
  430. * to the map are reflected in the set, and vice-versa. (If the map is
  431. * modified while an iteration over the set is in progress, the results of
  432. * the iteration are undefined.) The set supports element removal, which
  433. * removes the corresponding entry from the map, via the
  434. * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>,
  435. * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not support
  436. * the <tt>add</tt> or <tt>addAll</tt> operations.
  437. *
  438. * @return a set view of the mappings contained in this map.
  439. */
  440. public abstract Set<Entry<K,V>> entrySet();
  441. // Comparison and hashing
  442. /**
  443. * Compares the specified object with this map for equality. Returns
  444. * <tt>true</tt> if the given object is also a map and the two maps
  445. * represent the same mappings. More formally, two maps <tt>t1</tt> and
  446. * <tt>t2</tt> represent the same mappings if
  447. * <tt>t1.keySet().equals(t2.keySet())</tt> and for every key <tt>k</tt>
  448. * in <tt>t1.keySet()</tt>, <tt> (t1.get(k)==null ? t2.get(k)==null :
  449. * t1.get(k).equals(t2.get(k))) </tt>. This ensures that the
  450. * <tt>equals</tt> method works properly across different implementations
  451. * of the map interface.<p>
  452. *
  453. * This implementation first checks if the specified object is this map;
  454. * if so it returns <tt>true</tt>. Then, it checks if the specified
  455. * object is a map whose size is identical to the size of this set; if
  456. * not, it returns <tt>false</tt>. If so, it iterates over this map's
  457. * <tt>entrySet</tt> collection, and checks that the specified map
  458. * contains each mapping that this map contains. If the specified map
  459. * fails to contain such a mapping, <tt>false</tt> is returned. If the
  460. * iteration completes, <tt>true</tt> is returned.
  461. *
  462. * @param o object to be compared for equality with this map.
  463. * @return <tt>true</tt> if the specified object is equal to this map.
  464. */
  465. public boolean equals(Object o) {
  466. if (o == this)
  467. return true;
  468. if (!(o instanceof Map))
  469. return false;
  470. Map<K,V> t = (Map<K,V>) o;
  471. if (t.size() != size())
  472. return false;
  473. try {
  474. Iterator<Entry<K,V>> i = entrySet().iterator();
  475. while (i.hasNext()) {
  476. Entry<K,V> e = i.next();
  477. K key = e.getKey();
  478. V value = e.getValue();
  479. if (value == null) {
  480. if (!(t.get(key)==null && t.containsKey(key)))
  481. return false;
  482. } else {
  483. if (!value.equals(t.get(key)))
  484. return false;
  485. }
  486. }
  487. } catch(ClassCastException unused) {
  488. return false;
  489. } catch(NullPointerException unused) {
  490. return false;
  491. }
  492. return true;
  493. }
  494. /**
  495. * Returns the hash code value for this map. The hash code of a map is
  496. * defined to be the sum of the hash codes of each entry in the map's
  497. * <tt>entrySet()</tt> view. This ensures that <tt>t1.equals(t2)</tt>
  498. * implies that <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps
  499. * <tt>t1</tt> and <tt>t2</tt>, as required by the general contract of
  500. * Object.hashCode.<p>
  501. *
  502. * This implementation iterates over <tt>entrySet()</tt>, calling
  503. * <tt>hashCode</tt> on each element (entry) in the Collection, and adding
  504. * up the results.
  505. *
  506. * @return the hash code value for this map.
  507. * @see Map.Entry#hashCode()
  508. * @see Object#hashCode()
  509. * @see Object#equals(Object)
  510. * @see Set#equals(Object)
  511. */
  512. public int hashCode() {
  513. int h = 0;
  514. Iterator<Entry<K,V>> i = entrySet().iterator();
  515. while (i.hasNext())
  516. h += i.next().hashCode();
  517. return h;
  518. }
  519. /**
  520. * Returns a string representation of this map. The string representation
  521. * consists of a list of key-value mappings in the order returned by the
  522. * map's <tt>entrySet</tt> view's iterator, enclosed in braces
  523. * (<tt>"{}"</tt>). Adjacent mappings are separated by the characters
  524. * <tt>", "</tt> (comma and space). Each key-value mapping is rendered as
  525. * the key followed by an equals sign (<tt>"="</tt>) followed by the
  526. * associated value. Keys and values are converted to strings as by
  527. * <tt>String.valueOf(Object)</tt>.<p>
  528. *
  529. * This implementation creates an empty string buffer, appends a left
  530. * brace, and iterates over the map's <tt>entrySet</tt> view, appending
  531. * the string representation of each <tt>map.entry</tt> in turn. After
  532. * appending each entry except the last, the string <tt>", "</tt> is
  533. * appended. Finally a right brace is appended. A string is obtained
  534. * from the stringbuffer, and returned.
  535. *
  536. * @return a String representation of this map.
  537. */
  538. public String toString() {
  539. StringBuffer buf = new StringBuffer();
  540. buf.append("{");
  541. Iterator<Entry<K,V>> i = entrySet().iterator();
  542. boolean hasNext = i.hasNext();
  543. while (hasNext) {
  544. Entry<K,V> e = i.next();
  545. K key = e.getKey();
  546. V value = e.getValue();
  547. if (key == this)
  548. buf.append("(this Map)");
  549. else
  550. buf.append(key);
  551. buf.append("=");
  552. if (value == this)
  553. buf.append("(this Map)");
  554. else
  555. buf.append(value);
  556. hasNext = i.hasNext();
  557. if (hasNext)
  558. buf.append(", ");
  559. }
  560. buf.append("}");
  561. return buf.toString();
  562. }
  563. /**
  564. * Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys
  565. * and values themselves are not cloned.
  566. *
  567. * @return a shallow copy of this map.
  568. */
  569. protected Object clone() throws CloneNotSupportedException {
  570. AbstractMap<K,V> result = (AbstractMap<K,V>)super.clone();
  571. result.keySet = null;
  572. result.values = null;
  573. return result;
  574. }
  575. /**
  576. * This should be made public as soon as possible. It greatly simplifies
  577. * the task of implementing Map.
  578. */
  579. static class SimpleEntry<K,V> implements Entry<K,V> {
  580. K key;
  581. V value;
  582. public SimpleEntry(K key, V value) {
  583. this.key = key;
  584. this.value = value;
  585. }
  586. public SimpleEntry(Entry<K,V> e) {
  587. this.key = e.getKey();
  588. this.value = e.getValue();
  589. }
  590. public K getKey() {
  591. return key;
  592. }
  593. public V getValue() {
  594. return value;
  595. }
  596. public V setValue(V value) {
  597. V oldValue = this.value;
  598. this.value = value;
  599. return oldValue;
  600. }
  601. public boolean equals(Object o) {
  602. if (!(o instanceof Map.Entry))
  603. return false;
  604. Map.Entry e = (Map.Entry)o;
  605. return eq(key, e.getKey()) && eq(value, e.getValue());
  606. }
  607. public int hashCode() {
  608. return ((key == null) ? 0 : key.hashCode()) ^
  609. ((value == null) ? 0 : value.hashCode());
  610. }
  611. public String toString() {
  612. return key + "=" + value;
  613. }
  614. private static boolean eq(Object o1, Object o2) {
  615. return (o1 == null ? o2 == null : o1.equals(o2));
  616. }
  617. }
  618. }