ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/AbstractMap.java
Revision: 1.10
Committed: Mon May 16 05:03:18 2005 UTC (19 years ago) by jsr166
Branch: MAIN
Changes since 1.9: +6 -6 lines
Log Message:
doc fixes

File Contents

# Content
1 /*
2 * %W% %E%
3 *
4 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6 */
7
8 package java.util;
9 import java.util.Map.Entry;
10
11 /**
12 * This class provides a skeletal implementation of the <tt>Map</tt>
13 * interface, to minimize the effort required to implement this interface.
14 *
15 * <p>To implement an unmodifiable map, the programmer needs only to extend this
16 * class and provide an implementation for the <tt>entrySet</tt> method, which
17 * returns a set-view of the map's mappings. Typically, the returned set
18 * will, in turn, be implemented atop <tt>AbstractSet</tt>. This set should
19 * not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator
20 * should not support the <tt>remove</tt> method.
21 *
22 * <p>To implement a modifiable map, the programmer must additionally override
23 * this class's <tt>put</tt> method (which otherwise throws an
24 * <tt>UnsupportedOperationException</tt>), and the iterator returned by
25 * <tt>entrySet().iterator()</tt> must additionally implement its
26 * <tt>remove</tt> method.
27 *
28 * <p>The programmer should generally provide a void (no argument) and map
29 * constructor, as per the recommendation in the <tt>Map</tt> interface
30 * specification.
31 *
32 * <p>The documentation for each non-abstract methods in this class describes its
33 * implementation in detail. Each of these methods may be overridden if the
34 * map being implemented admits a more efficient implementation.
35 *
36 * <p>This class is a member of the
37 * <a href="{@docRoot}/../guide/collections/index.html">
38 * Java Collections Framework</a>.
39 *
40 * @param <K> the type of keys maintained by this map
41 * @param <V> the type of mapped values
42 *
43 * @author Josh Bloch
44 * @author Neal Gafter
45 * @version %I%, %G%
46 * @see Map
47 * @see Collection
48 * @since 1.2
49 */
50
51 public abstract class AbstractMap<K,V> implements Map<K,V> {
52 /**
53 * Sole constructor. (For invocation by subclass constructors, typically
54 * implicit.)
55 */
56 protected AbstractMap() {
57 }
58
59 // Query Operations
60
61 /**
62 * {@inheritDoc}
63 *
64 * <p>This implementation returns <tt>entrySet().size()</tt>.
65 */
66 public int size() {
67 return entrySet().size();
68 }
69
70 /**
71 * {@inheritDoc}
72 *
73 * <p>This implementation returns <tt>size() == 0</tt>.
74 */
75 public boolean isEmpty() {
76 return size() == 0;
77 }
78
79 /**
80 * {@inheritDoc}
81 *
82 * <p>This implementation iterates over <tt>entrySet()</tt> searching
83 * for an entry with the specified value. If such an entry is found,
84 * <tt>true</tt> is returned. If the iteration terminates without
85 * finding such an entry, <tt>false</tt> is returned. Note that this
86 * implementation requires linear time in the size of the map.
87 *
88 * @throws ClassCastException {@inheritDoc}
89 * @throws NullPointerException {@inheritDoc}
90 */
91 public boolean containsValue(Object value) {
92 Iterator<Entry<K,V>> i = entrySet().iterator();
93 if (value==null) {
94 while (i.hasNext()) {
95 Entry<K,V> e = i.next();
96 if (e.getValue()==null)
97 return true;
98 }
99 } else {
100 while (i.hasNext()) {
101 Entry<K,V> e = i.next();
102 if (value.equals(e.getValue()))
103 return true;
104 }
105 }
106 return false;
107 }
108
109 /**
110 * {@inheritDoc}
111 *
112 * <p>This implementation iterates over <tt>entrySet()</tt> searching
113 * for an entry with the specified key. If such an entry is found,
114 * <tt>true</tt> is returned. If the iteration terminates without
115 * finding such an entry, <tt>false</tt> is returned. Note that this
116 * implementation requires linear time in the size of the map; many
117 * implementations will override this method.
118 *
119 * @throws ClassCastException {@inheritDoc}
120 * @throws NullPointerException {@inheritDoc}
121 */
122 public boolean containsKey(Object key) {
123 Iterator<Map.Entry<K,V>> i = entrySet().iterator();
124 if (key==null) {
125 while (i.hasNext()) {
126 Entry<K,V> e = i.next();
127 if (e.getKey()==null)
128 return true;
129 }
130 } else {
131 while (i.hasNext()) {
132 Entry<K,V> e = i.next();
133 if (key.equals(e.getKey()))
134 return true;
135 }
136 }
137 return false;
138 }
139
140 /**
141 * {@inheritDoc}
142 *
143 * <p>This implementation iterates over <tt>entrySet()</tt> searching
144 * for an entry with the specified key. If such an entry is found,
145 * the entry's value is returned. If the iteration terminates without
146 * finding such an entry, <tt>null</tt> is returned. Note that this
147 * implementation requires linear time in the size of the map; many
148 * implementations will override this method.
149 *
150 * @throws ClassCastException {@inheritDoc}
151 * @throws NullPointerException {@inheritDoc}
152 */
153 public V get(Object key) {
154 Iterator<Entry<K,V>> i = entrySet().iterator();
155 if (key==null) {
156 while (i.hasNext()) {
157 Entry<K,V> e = i.next();
158 if (e.getKey()==null)
159 return e.getValue();
160 }
161 } else {
162 while (i.hasNext()) {
163 Entry<K,V> e = i.next();
164 if (key.equals(e.getKey()))
165 return e.getValue();
166 }
167 }
168 return null;
169 }
170
171
172 // Modification Operations
173
174 /**
175 * {@inheritDoc}
176 *
177 * <p>This implementation always throws an
178 * <tt>UnsupportedOperationException</tt>.
179 *
180 * @throws UnsupportedOperationException {@inheritDoc}
181 * @throws ClassCastException {@inheritDoc}
182 * @throws NullPointerException {@inheritDoc}
183 * @throws IllegalArgumentException {@inheritDoc}
184 */
185 public V put(K key, V value) {
186 throw new UnsupportedOperationException();
187 }
188
189 /**
190 * {@inheritDoc}
191 *
192 * <p>This implementation iterates over <tt>entrySet()</tt> searching for an
193 * entry with the specified key. If such an entry is found, its value is
194 * obtained with its <tt>getValue</tt> operation, the entry is removed
195 * from the collection (and the backing map) with the iterator's
196 * <tt>remove</tt> operation, and the saved value is returned. If the
197 * iteration terminates without finding such an entry, <tt>null</tt> is
198 * returned. Note that this implementation requires linear time in the
199 * size of the map; many implementations will override this method.
200 *
201 * <p>Note that this implementation throws an
202 * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
203 * iterator does not support the <tt>remove</tt> method and this map
204 * contains a mapping for the specified key.
205 *
206 * @throws UnsupportedOperationException {@inheritDoc}
207 * @throws ClassCastException {@inheritDoc}
208 * @throws NullPointerException {@inheritDoc}
209 */
210 public V remove(Object key) {
211 Iterator<Entry<K,V>> i = entrySet().iterator();
212 Entry<K,V> correctEntry = null;
213 if (key==null) {
214 while (correctEntry==null && i.hasNext()) {
215 Entry<K,V> e = i.next();
216 if (e.getKey()==null)
217 correctEntry = e;
218 }
219 } else {
220 while (correctEntry==null && i.hasNext()) {
221 Entry<K,V> e = i.next();
222 if (key.equals(e.getKey()))
223 correctEntry = e;
224 }
225 }
226
227 V oldValue = null;
228 if (correctEntry !=null) {
229 oldValue = correctEntry.getValue();
230 i.remove();
231 }
232 return oldValue;
233 }
234
235
236 // Bulk Operations
237
238 /**
239 * {@inheritDoc}
240 *
241 * <p>This implementation iterates over the specified map's
242 * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
243 * operation once for each entry returned by the iteration.
244 *
245 * <p>Note that this implementation throws an
246 * <tt>UnsupportedOperationException</tt> if this map does not support
247 * the <tt>put</tt> operation and the specified map is nonempty.
248 *
249 * @throws UnsupportedOperationException {@inheritDoc}
250 * @throws ClassCastException {@inheritDoc}
251 * @throws NullPointerException {@inheritDoc}
252 * @throws IllegalArgumentException {@inheritDoc}
253 */
254 public void putAll(Map<? extends K, ? extends V> m) {
255 Iterator<? extends Entry<? extends K, ? extends V>> i = m.entrySet().iterator();
256 while (i.hasNext()) {
257 Entry<? extends K, ? extends V> e = i.next();
258 put(e.getKey(), e.getValue());
259 }
260 }
261
262 /**
263 * {@inheritDoc}
264 *
265 * <p>This implementation calls <tt>entrySet().clear()</tt>.
266 *
267 * <p>Note that this implementation throws an
268 * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
269 * does not support the <tt>clear</tt> operation.
270 *
271 * @throws UnsupportedOperationException {@inheritDoc}
272 */
273 public void clear() {
274 entrySet().clear();
275 }
276
277
278 // Views
279
280 /**
281 * Each of these fields are initialized to contain an instance of the
282 * appropriate view the first time this view is requested. The views are
283 * stateless, so there's no reason to create more than one of each.
284 */
285 transient volatile Set<K> keySet = null;
286 transient volatile Collection<V> values = null;
287
288 /**
289 * {@inheritDoc}
290 *
291 * <p>This implementation returns a set that subclasses {@link AbstractSet}.
292 * The subclass's iterator method returns a "wrapper object" over this
293 * map's <tt>entrySet()</tt> iterator. The <tt>size</tt> method
294 * delegates to this map's <tt>size</tt> method and the
295 * <tt>contains</tt> method delegates to this map's
296 * <tt>containsKey</tt> method.
297 *
298 * <p>The set is created the first time this method is called,
299 * and returned in response to all subsequent calls. No synchronization
300 * is performed, so there is a slight chance that multiple calls to this
301 * method will not all return the same set.
302 */
303 public Set<K> keySet() {
304 if (keySet == null) {
305 keySet = new AbstractSet<K>() {
306 public Iterator<K> iterator() {
307 return new Iterator<K>() {
308 private Iterator<Entry<K,V>> i = entrySet().iterator();
309
310 public boolean hasNext() {
311 return i.hasNext();
312 }
313
314 public K next() {
315 return i.next().getKey();
316 }
317
318 public void remove() {
319 i.remove();
320 }
321 };
322 }
323
324 public int size() {
325 return AbstractMap.this.size();
326 }
327
328 public boolean contains(Object k) {
329 return AbstractMap.this.containsKey(k);
330 }
331 };
332 }
333 return keySet;
334 }
335
336 /**
337 * {@inheritDoc}
338 *
339 * <p>This implementation returns a collection that subclasses {@link
340 * AbstractCollection}. The subclass's iterator method returns a
341 * "wrapper object" over this map's <tt>entrySet()</tt> iterator.
342 * The <tt>size</tt> method delegates to this map's <tt>size</tt>
343 * method and the <tt>contains</tt> method delegates to this map's
344 * <tt>containsValue</tt> method.
345 *
346 * <p>The collection is created the first time this method is called, and
347 * returned in response to all subsequent calls. No synchronization is
348 * performed, so there is a slight chance that multiple calls to this
349 * method will not all return the same collection.
350 */
351 public Collection<V> values() {
352 if (values == null) {
353 values = new AbstractCollection<V>() {
354 public Iterator<V> iterator() {
355 return new Iterator<V>() {
356 private Iterator<Entry<K,V>> i = entrySet().iterator();
357
358 public boolean hasNext() {
359 return i.hasNext();
360 }
361
362 public V next() {
363 return i.next().getValue();
364 }
365
366 public void remove() {
367 i.remove();
368 }
369 };
370 }
371
372 public int size() {
373 return AbstractMap.this.size();
374 }
375
376 public boolean contains(Object v) {
377 return AbstractMap.this.containsValue(v);
378 }
379 };
380 }
381 return values;
382 }
383
384 public abstract Set<Entry<K,V>> entrySet();
385
386
387 // Comparison and hashing
388
389 /**
390 * {@inheritDoc}
391 *
392 * <p>This implementation first checks if the specified object is this map;
393 * if so it returns <tt>true</tt>. Then, it checks if the specified
394 * object is a map whose size is identical to the size of this map; if
395 * not, it returns <tt>false</tt>. If so, it iterates over this map's
396 * <tt>entrySet</tt> collection, and checks that the specified map
397 * contains each mapping that this map contains. If the specified map
398 * fails to contain such a mapping, <tt>false</tt> is returned. If the
399 * iteration completes, <tt>true</tt> is returned.
400 */
401 public boolean equals(Object o) {
402 if (o == this)
403 return true;
404
405 if (!(o instanceof Map))
406 return false;
407 Map<K,V> t = (Map<K,V>) o;
408 if (t.size() != size())
409 return false;
410
411 try {
412 Iterator<Entry<K,V>> i = entrySet().iterator();
413 while (i.hasNext()) {
414 Entry<K,V> e = i.next();
415 K key = e.getKey();
416 V value = e.getValue();
417 if (value == null) {
418 if (!(t.get(key)==null && t.containsKey(key)))
419 return false;
420 } else {
421 if (!value.equals(t.get(key)))
422 return false;
423 }
424 }
425 } catch (ClassCastException unused) {
426 return false;
427 } catch (NullPointerException unused) {
428 return false;
429 }
430
431 return true;
432 }
433
434 /**
435 * {@inheritDoc}
436 *
437 * <p>This implementation iterates over <tt>entrySet()</tt>, calling
438 * <tt>hashCode()</tt> on each element (entry) in the set, and
439 * adding up the results.
440 *
441 * @see Map.Entry#hashCode()
442 * @see Object#hashCode()
443 * @see Object#equals(Object)
444 * @see Set#equals(Object)
445 */
446 public int hashCode() {
447 int h = 0;
448 Iterator<Entry<K,V>> i = entrySet().iterator();
449 while (i.hasNext())
450 h += i.next().hashCode();
451 return h;
452 }
453
454 /**
455 * Returns a string representation of this map. The string representation
456 * consists of a list of key-value mappings in the order returned by the
457 * map's <tt>entrySet</tt> view's iterator, enclosed in braces
458 * (<tt>"{}"</tt>). Adjacent mappings are separated by the characters
459 * <tt>", "</tt> (comma and space). Each key-value mapping is rendered as
460 * the key followed by an equals sign (<tt>"="</tt>) followed by the
461 * associated value. Keys and values are converted to strings as by
462 * <tt>String.valueOf(Object)</tt>.<p>
463 *
464 * This implementation creates an empty string buffer, appends a left
465 * brace, and iterates over the map's <tt>entrySet</tt> view, appending
466 * the string representation of each <tt>map.entry</tt> in turn. After
467 * appending each entry except the last, the string <tt>", "</tt> is
468 * appended. Finally a right brace is appended. A string is obtained
469 * from the stringbuffer, and returned.
470 *
471 * @return a String representation of this map.
472 */
473 public String toString() {
474 StringBuffer buf = new StringBuffer();
475 buf.append("{");
476
477 Iterator<Entry<K,V>> i = entrySet().iterator();
478 boolean hasNext = i.hasNext();
479 while (hasNext) {
480 Entry<K,V> e = i.next();
481 K key = e.getKey();
482 V value = e.getValue();
483 if (key == this)
484 buf.append("(this Map)");
485 else
486 buf.append(key);
487 buf.append("=");
488 if (value == this)
489 buf.append("(this Map)");
490 else
491 buf.append(value);
492 hasNext = i.hasNext();
493 if (hasNext)
494 buf.append(", ");
495 }
496
497 buf.append("}");
498 return buf.toString();
499 }
500
501 /**
502 * Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys
503 * and values themselves are not cloned.
504 *
505 * @return a shallow copy of this map
506 */
507 protected Object clone() throws CloneNotSupportedException {
508 AbstractMap<K,V> result = (AbstractMap<K,V>)super.clone();
509 result.keySet = null;
510 result.values = null;
511 return result;
512 }
513
514 /**
515 * Utility method for SimpleEntry and SimpleImmutableEntry.
516 * Test for equality, checking for nulls.
517 */
518 private static boolean eq(Object o1, Object o2) {
519 return (o1 == null ? o2 == null : o1.equals(o2));
520 }
521
522 // Implementation Note: SimpleEntry and SimpleImmutableEntry
523 // are distinct unrelated classes, even though they share
524 // some code. Since you can't add or subtract final-ness
525 // of a field in a subclass, they can't share representations,
526 // and the amount of duplicated code is too small to warrant
527 // exposing a common abstract class.
528
529
530 /**
531 * An Entry maintaining a key and a value. The value may be
532 * changed using the <tt>setValue</tt> method. This class
533 * facilitates the process of building custom map
534 * implementations. For example, it may be convenient to return
535 * arrays of <tt>SimpleEntry</tt> instances in method
536 * <tt>Map.entrySet().toArray</tt>
537 */
538 public static class SimpleEntry<K,V> implements Entry<K,V> {
539 private final K key;
540 private V value;
541
542 /**
543 * Creates an entry representing a mapping from the specified
544 * key to the specified value.
545 *
546 * @param key the key represented by this entry
547 * @param value the value represented by this entry
548 */
549 public SimpleEntry(K key, V value) {
550 this.key = key;
551 this.value = value;
552 }
553
554 /**
555 * Creates an entry representing the same mapping as the
556 * specified entry.
557 *
558 * @param entry the entry to copy.
559 */
560 public SimpleEntry(Entry<? extends K, ? extends V> entry) {
561 this.key = entry.getKey();
562 this.value = entry.getValue();
563 }
564
565 /**
566 * Returns the key corresponding to this entry.
567 *
568 * @return the key corresponding to this entry
569 */
570 public K getKey() {
571 return key;
572 }
573
574 /**
575 * Returns the value corresponding to this entry.
576 *
577 * @return the value corresponding to this entry
578 */
579 public V getValue() {
580 return value;
581 }
582
583 /**
584 * Replaces the value corresponding to this entry with the specified
585 * value.
586 *
587 * @param value new value to be stored in this entry
588 * @return the old value corresponding to the entry
589 */
590 public V setValue(V value) {
591 V oldValue = this.value;
592 this.value = value;
593 return oldValue;
594 }
595
596 public boolean equals(Object o) {
597 if (!(o instanceof Map.Entry))
598 return false;
599 Map.Entry e = (Map.Entry)o;
600 return eq(key, e.getKey()) && eq(value, e.getValue());
601 }
602
603 public int hashCode() {
604 return ((key == null) ? 0 : key.hashCode()) ^
605 ((value == null) ? 0 : value.hashCode());
606 }
607
608 /**
609 * Returns a String representation of this map entry. This
610 * implementation returns the string representation of this
611 * entry's key followed by the equals character ("<tt>=</tt>")
612 * followed by the string representation of this entry's value.
613 *
614 * @return a String representation of this map entry
615 */
616 public String toString() {
617 return key + "=" + value;
618 }
619
620 }
621
622 /**
623 * An Entry maintaining an immutable key and value, This class
624 * does not support method <tt>setValue</tt>. This class may be
625 * convenient in methods that return thread-safe snapshots of
626 * key-value mappings.
627 */
628 public static class SimpleImmutableEntry<K,V> implements Entry<K,V> {
629 private final K key;
630 private final V value;
631
632 /**
633 * Creates an entry representing a mapping from the specified
634 * key to the specified value.
635 *
636 * @param key the key represented by this entry
637 * @param value the value represented by this entry
638 */
639 public SimpleImmutableEntry(K key, V value) {
640 this.key = key;
641 this.value = value;
642 }
643
644 /**
645 * Creates an entry representing the same mapping as the
646 * specified entry.
647 *
648 * @param entry the entry to copy
649 */
650 public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
651 this.key = entry.getKey();
652 this.value = entry.getValue();
653 }
654
655 /**
656 * Returns the key corresponding to this entry.
657 *
658 * @return the key corresponding to this entry
659 */
660 public K getKey() {
661 return key;
662 }
663
664 /**
665 * Returns the value corresponding to this entry.
666 *
667 * @return the value corresponding to this entry
668 */
669 public V getValue() {
670 return value;
671 }
672
673 /**
674 * Replaces the value corresponding to this entry with the specified
675 * value (optional operation). This implementation simply throws
676 * <tt>UnsupportedOperationException</tt>, as this class implements
677 * an <i>immutable</i> map entry.
678 *
679 * @param value new value to be stored in this entry
680 * @return (Does not return)
681 * @throws UnsupportedOperationException always
682 */
683 public V setValue(V value) {
684 throw new UnsupportedOperationException();
685 }
686
687 public boolean equals(Object o) {
688 if (!(o instanceof Map.Entry))
689 return false;
690 Map.Entry e = (Map.Entry)o;
691 return eq(key, e.getKey()) && eq(value, e.getValue());
692 }
693
694 public int hashCode() {
695 return ((key == null) ? 0 : key.hashCode()) ^
696 ((value == null) ? 0 : value.hashCode());
697 }
698
699 /**
700 * Returns a String representation of this map entry. This
701 * implementation returns the string representation of this
702 * entry's key followed by the equals character ("<tt>=</tt>")
703 * followed by the string representation of this entry's value.
704 *
705 * @return a String representation of this map entry
706 */
707 public String toString() {
708 return key + "=" + value;
709 }
710
711 }
712
713 }