ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/AbstractMap.java
Revision: 1.7
Committed: Wed Apr 27 01:39:03 2005 UTC (19 years ago) by jsr166
Branch: MAIN
Changes since 1.6: +73 -85 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. <p>
14 *
15 * 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.<p>
21 *
22 * 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.<p>
27 *
28 * 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.<p>
31 *
32 * 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.<p>
35 *
36 * 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 * Returns the number of key-value mappings in this map. If the map
63 * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
64 * <tt>Integer.MAX_VALUE</tt>.<p>
65 *
66 * This implementation returns <tt>entrySet().size()</tt>.
67 *
68 * @return the number of key-value mappings in this map.
69 */
70 public int size() {
71 return entrySet().size();
72 }
73
74 /**
75 * Returns <tt>true</tt> if this map contains no key-value mappings. <p>
76 *
77 * This implementation returns <tt>size() == 0</tt>.
78 *
79 * @return <tt>true</tt> if this map contains no key-value mappings.
80 */
81 public boolean isEmpty() {
82 return size() == 0;
83 }
84
85 /**
86 * Returns <tt>true</tt> if this map maps one or more keys to this value.
87 * More formally, returns <tt>true</tt> if and only if this map contains
88 * at least one mapping to a value <tt>v</tt> such that <tt>(value==null ?
89 * v==null : value.equals(v))</tt>. This operation will probably require
90 * time linear in the map size for most implementations of <tt>Map</tt>.<p>
91 *
92 * This implementation iterates over <tt>entrySet()</tt> searching for an entry
93 * with the specified value. If such an entry is found, <tt>true</tt> is
94 * returned. If the iteration terminates without finding such an entry,
95 * <tt>false</tt> is returned. Note that this implementation requires
96 * linear time in the size of the map.
97 *
98 * @param value value whose presence in this map is to be tested.
99 *
100 * @return <tt>true</tt> if this map maps one or more keys to this value.
101 */
102 public boolean containsValue(Object value) {
103 Iterator<Entry<K,V>> i = entrySet().iterator();
104 if (value==null) {
105 while (i.hasNext()) {
106 Entry<K,V> e = i.next();
107 if (e.getValue()==null)
108 return true;
109 }
110 } else {
111 while (i.hasNext()) {
112 Entry<K,V> e = i.next();
113 if (value.equals(e.getValue()))
114 return true;
115 }
116 }
117 return false;
118 }
119
120 /**
121 * Returns <tt>true</tt> if this map contains a mapping for the specified
122 * key. <p>
123 *
124 * This implementation iterates over <tt>entrySet()</tt> searching for an
125 * entry with the specified key. If such an entry is found, <tt>true</tt>
126 * is returned. If the iteration terminates without finding such an
127 * entry, <tt>false</tt> is returned. Note that this implementation
128 * requires linear time in the size of the map; many implementations will
129 * override this method.
130 *
131 * @param key key whose presence in this map is to be tested.
132 * @return <tt>true</tt> if this map contains a mapping for the specified
133 * key.
134 * @throws NullPointerException if <tt>key</tt> is <tt>null</tt>
135 * and this map does not permit <tt>null</tt> keys.
136 */
137 public boolean containsKey(Object key) {
138 Iterator<Map.Entry<K,V>> i = entrySet().iterator();
139 if (key==null) {
140 while (i.hasNext()) {
141 Entry<K,V> e = i.next();
142 if (e.getKey()==null)
143 return true;
144 }
145 } else {
146 while (i.hasNext()) {
147 Entry<K,V> e = 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 <tt>key</tt> is <tt>null</tt>
174 * and this map does not permit <tt>null</tt> keys.
175 *
176 * @see #containsKey(Object)
177 */
178 public V get(Object key) {
179 Iterator<Entry<K,V>> i = entrySet().iterator();
180 if (key==null) {
181 while (i.hasNext()) {
182 Entry<K,V> e = i.next();
183 if (e.getKey()==null)
184 return e.getValue();
185 }
186 } else {
187 while (i.hasNext()) {
188 Entry<K,V> e = 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 the 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
217 * is not supported by this map.
218 * @throws ClassCastException if the class of the specified key or value
219 * prevents it from being stored in this map.
220 * @throws IllegalArgumentException if some aspect of this key or value
221 * prevents it from being stored in this map.
222 * @throws NullPointerException if this map does not permit <tt>null</tt>
223 * keys or values, and the specified key or value is <tt>null</tt>.
224 */
225 public V put(K key, V value) {
226 throw new UnsupportedOperationException();
227 }
228
229 /**
230 * Removes the mapping for this key from this map if present (optional
231 * operation). <p>
232 *
233 * This implementation iterates over <tt>entrySet()</tt> searching for an
234 * entry with the specified key. If such an entry is found, its value is
235 * obtained with its <tt>getValue</tt> operation, the entry is removed
236 * from the collection (and the backing map) with the iterator's
237 * <tt>remove</tt> operation, and the saved value is returned. If the
238 * iteration terminates without finding such an entry, <tt>null</tt> is
239 * returned. Note that this implementation requires linear time in the
240 * size of the map; many implementations will override this method.<p>
241 *
242 * Note that this implementation throws an
243 * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt> iterator
244 * does not support the <tt>remove</tt> method and this map contains a
245 * mapping for the specified key.
246 *
247 * @param key key whose mapping is to be removed from the map.
248 * @return the previous value associated with specified key, or <tt>null</tt>
249 * if there was no entry for key. (A <tt>null</tt> return can
250 * also indicate that the map previously associated <tt>null</tt>
251 * with the specified key, if the implementation supports
252 * <tt>null</tt> values.)
253 * @throws UnsupportedOperationException if the <tt>remove</tt> operation
254 * is not supported by this map.
255 */
256 public V remove(Object key) {
257 Iterator<Entry<K,V>> i = entrySet().iterator();
258 Entry<K,V> correctEntry = null;
259 if (key==null) {
260 while (correctEntry==null && i.hasNext()) {
261 Entry<K,V> e = i.next();
262 if (e.getKey()==null)
263 correctEntry = e;
264 }
265 } else {
266 while (correctEntry==null && i.hasNext()) {
267 Entry<K,V> e = i.next();
268 if (key.equals(e.getKey()))
269 correctEntry = e;
270 }
271 }
272
273 V oldValue = null;
274 if (correctEntry !=null) {
275 oldValue = correctEntry.getValue();
276 i.remove();
277 }
278 return oldValue;
279 }
280
281
282 // Bulk Operations
283
284 /**
285 * Copies all of the mappings from the specified map to this map
286 * (optional operation). These mappings will replace any mappings that
287 * this map had for any of the keys currently in the specified map.<p>
288 *
289 * This implementation iterates over the specified map's
290 * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
291 * operation once for each entry returned by the iteration.<p>
292 *
293 * Note that this implementation throws an
294 * <tt>UnsupportedOperationException</tt> if this map does not support
295 * the <tt>put</tt> operation and the specified map is nonempty.
296 *
297 * @param t mappings to be stored in this map.
298 *
299 * @throws UnsupportedOperationException if the <tt>putAll</tt> operation
300 * is not supported by this map.
301 * @throws ClassCastException if the class of a key or value in the
302 * specified map prevents it from being stored in this map.
303 * @throws IllegalArgumentException if some aspect of a key or value in
304 * the specified map prevents it from being stored in this map.
305 * @throws NullPointerException if the specified map is <tt>null</tt>, or if
306 * this map does not permit <tt>null</tt> keys or values, and the
307 * specified map contains <tt>null</tt> keys or values.
308 */
309 public void putAll(Map<? extends K, ? extends V> t) {
310 Iterator<? extends Entry<? extends K, ? extends V>> i = t.entrySet().iterator();
311 while (i.hasNext()) {
312 Entry<? extends K, ? extends V> e = i.next();
313 put(e.getKey(), e.getValue());
314 }
315 }
316
317 /**
318 * Removes all mappings from this map (optional operation). <p>
319 *
320 * This implementation calls <tt>entrySet().clear()</tt>.
321 *
322 * Note that this implementation throws an
323 * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
324 * does not support the <tt>clear</tt> operation.
325 *
326 * @throws UnsupportedOperationException if the <tt>clear</tt> operation
327 * is not supported by this map.
328 */
329 public void clear() {
330 entrySet().clear();
331 }
332
333
334 // Views
335
336 /**
337 * Each of these fields are initialized to contain an instance of the
338 * appropriate view the first time this view is requested. The views are
339 * stateless, so there's no reason to create more than one of each.
340 */
341 transient volatile Set<K> keySet = null;
342 transient volatile Collection<V> values = null;
343
344 /**
345 * Returns a {@link Set} view of the keys contained in this map.
346 * The set is backed by the map, so changes to the map are
347 * reflected in the set, and vice-versa. If the map is modified
348 * while an iteration over the set is in progress (except through
349 * the iterator's own <tt>remove</tt> operation), the results of
350 * the iteration are undefined. The set supports element removal,
351 * which removes the corresponding mapping from the map, via the
352 * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
353 * <tt>removeAll</tt> <tt>retainAll</tt>, and <tt>clear</tt>
354 * operations. It does not support the add or <tt>addAll</tt>
355 * operations.
356 *
357 * <p>This implementation returns a set that subclasses {@link AbstractSet}.
358 * The subclass's iterator method returns a "wrapper object" over this
359 * map's <tt>entrySet()</tt> iterator. The <tt>size</tt> method
360 * delegates to this map's <tt>size</tt> method and the
361 * <tt>contains</tt> method delegates to this map's
362 * <tt>containsKey</tt> method.
363 *
364 * <p>The set is created the first time this method is called,
365 * and returned in response to all subsequent calls. No synchronization
366 * is performed, so there is a slight chance that multiple calls to this
367 * method will not all return the same set.
368 */
369 public Set<K> keySet() {
370 if (keySet == null) {
371 keySet = new AbstractSet<K>() {
372 public Iterator<K> iterator() {
373 return new Iterator<K>() {
374 private Iterator<Entry<K,V>> i = entrySet().iterator();
375
376 public boolean hasNext() {
377 return i.hasNext();
378 }
379
380 public K next() {
381 return i.next().getKey();
382 }
383
384 public void remove() {
385 i.remove();
386 }
387 };
388 }
389
390 public int size() {
391 return AbstractMap.this.size();
392 }
393
394 public boolean contains(Object k) {
395 return AbstractMap.this.containsKey(k);
396 }
397 };
398 }
399 return keySet;
400 }
401
402 /**
403 * Returns a {@link Collection} view of the values contained in this map.
404 * The collection is backed by the map, so changes to the map are
405 * reflected in the collection, and vice-versa. If the map is
406 * modified while an iteration over the collection is in progress
407 * (except through the iterator's own <tt>remove</tt> operation),
408 * the results of the iteration are undefined. The collection
409 * supports element removal, which removes the corresponding
410 * mapping from the map, via the <tt>Iterator.remove</tt>,
411 * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
412 * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
413 * support the add or <tt>addAll</tt> operations.
414 *
415 * <p>This implementation returns a collection that subclasses {@link
416 * AbstractCollection}. The subclass's iterator method returns a
417 * "wrapper object" over this map's <tt>entrySet()</tt> iterator.
418 * The <tt>size</tt> method delegates to this map's <tt>size</tt>
419 * method and the <tt>contains</tt> method delegates to this map's
420 * <tt>containsValue</tt> method.
421 *
422 * <p>The collection is created the first time this method is called, and
423 * returned in response to all subsequent calls. No synchronization is
424 * performed, so there is a slight chance that multiple calls to this
425 * method will not all return the same collection.
426 *
427 * @return a collection view of the values contained in this map.
428 */
429 public Collection<V> values() {
430 if (values == null) {
431 values = new AbstractCollection<V>() {
432 public Iterator<V> iterator() {
433 return new Iterator<V>() {
434 private Iterator<Entry<K,V>> i = entrySet().iterator();
435
436 public boolean hasNext() {
437 return i.hasNext();
438 }
439
440 public V next() {
441 return i.next().getValue();
442 }
443
444 public void remove() {
445 i.remove();
446 }
447 };
448 }
449
450 public int size() {
451 return AbstractMap.this.size();
452 }
453
454 public boolean contains(Object v) {
455 return AbstractMap.this.containsValue(v);
456 }
457 };
458 }
459 return values;
460 }
461
462 public abstract Set<Entry<K,V>> entrySet();
463
464
465 // Comparison and hashing
466
467 /**
468 * Compares the specified object with this map for equality. Returns
469 * <tt>true</tt> if the given object is also a map and the two maps
470 * represent the same mappings. More formally, two maps <tt>t1</tt> and
471 * <tt>t2</tt> represent the same mappings if
472 * <tt>t1.keySet().equals(t2.keySet())</tt> and for every key <tt>k</tt>
473 * in <tt>t1.keySet()</tt>, <tt> (t1.get(k)==null ? t2.get(k)==null :
474 * t1.get(k).equals(t2.get(k))) </tt>. This ensures that the
475 * <tt>equals</tt> method works properly across different implementations
476 * of the map interface.<p>
477 *
478 * This implementation first checks if the specified object is this map;
479 * if so it returns <tt>true</tt>. Then, it checks if the specified
480 * object is a map whose size is identical to the size of this set; if
481 * not, it returns <tt>false</tt>. If so, it iterates over this map's
482 * <tt>entrySet</tt> collection, and checks that the specified map
483 * contains each mapping that this map contains. If the specified map
484 * fails to contain such a mapping, <tt>false</tt> is returned. If the
485 * iteration completes, <tt>true</tt> is returned.
486 *
487 * @param o object to be compared for equality with this map.
488 * @return <tt>true</tt> if the specified object is equal to this map.
489 */
490 public boolean equals(Object o) {
491 if (o == this)
492 return true;
493
494 if (!(o instanceof Map))
495 return false;
496 Map<K,V> t = (Map<K,V>) o;
497 if (t.size() != size())
498 return false;
499
500 try {
501 Iterator<Entry<K,V>> i = entrySet().iterator();
502 while (i.hasNext()) {
503 Entry<K,V> e = i.next();
504 K key = e.getKey();
505 V value = e.getValue();
506 if (value == null) {
507 if (!(t.get(key)==null && t.containsKey(key)))
508 return false;
509 } else {
510 if (!value.equals(t.get(key)))
511 return false;
512 }
513 }
514 } catch (ClassCastException unused) {
515 return false;
516 } catch (NullPointerException unused) {
517 return false;
518 }
519
520 return true;
521 }
522
523 /**
524 * Returns the hash code value for this map. The hash code of a map is
525 * defined to be the sum of the hash codes of each entry in the map's
526 * <tt>entrySet()</tt> view. This ensures that <tt>t1.equals(t2)</tt>
527 * implies that <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps
528 * <tt>t1</tt> and <tt>t2</tt>, as required by the general contract of
529 * Object.hashCode.<p>
530 *
531 * This implementation iterates over <tt>entrySet()</tt>, calling
532 * <tt>hashCode</tt> on each element (entry) in the Collection, and adding
533 * up the results.
534 *
535 * @return the hash code value for this map.
536 * @see Map.Entry#hashCode()
537 * @see Object#hashCode()
538 * @see Object#equals(Object)
539 * @see Set#equals(Object)
540 */
541 public int hashCode() {
542 int h = 0;
543 Iterator<Entry<K,V>> i = entrySet().iterator();
544 while (i.hasNext())
545 h += i.next().hashCode();
546 return h;
547 }
548
549 /**
550 * Returns a string representation of this map. The string representation
551 * consists of a list of key-value mappings in the order returned by the
552 * map's <tt>entrySet</tt> view's iterator, enclosed in braces
553 * (<tt>"{}"</tt>). Adjacent mappings are separated by the characters
554 * <tt>", "</tt> (comma and space). Each key-value mapping is rendered as
555 * the key followed by an equals sign (<tt>"="</tt>) followed by the
556 * associated value. Keys and values are converted to strings as by
557 * <tt>String.valueOf(Object)</tt>.<p>
558 *
559 * This implementation creates an empty string buffer, appends a left
560 * brace, and iterates over the map's <tt>entrySet</tt> view, appending
561 * the string representation of each <tt>map.entry</tt> in turn. After
562 * appending each entry except the last, the string <tt>", "</tt> is
563 * appended. Finally a right brace is appended. A string is obtained
564 * from the stringbuffer, and returned.
565 *
566 * @return a String representation of this map.
567 */
568 public String toString() {
569 StringBuffer buf = new StringBuffer();
570 buf.append("{");
571
572 Iterator<Entry<K,V>> i = entrySet().iterator();
573 boolean hasNext = i.hasNext();
574 while (hasNext) {
575 Entry<K,V> e = i.next();
576 K key = e.getKey();
577 V value = e.getValue();
578 if (key == this)
579 buf.append("(this Map)");
580 else
581 buf.append(key);
582 buf.append("=");
583 if (value == this)
584 buf.append("(this Map)");
585 else
586 buf.append(value);
587 hasNext = i.hasNext();
588 if (hasNext)
589 buf.append(", ");
590 }
591
592 buf.append("}");
593 return buf.toString();
594 }
595
596 /**
597 * Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys
598 * and values themselves are not cloned.
599 *
600 * @return a shallow copy of this map.
601 */
602 protected Object clone() throws CloneNotSupportedException {
603 AbstractMap<K,V> result = (AbstractMap<K,V>)super.clone();
604 result.keySet = null;
605 result.values = null;
606 return result;
607 }
608
609 /**
610 * Utility method for SimpleEntry and SimpleImmutableEntry.
611 * Test for equality, checking for nulls.
612 */
613 private static boolean eq(Object o1, Object o2) {
614 return (o1 == null ? o2 == null : o1.equals(o2));
615 }
616
617 // Implementation Note: SimpleEntry and SimpleImmutableEntry
618 // are distinct unrelated classes, even though they share
619 // some code. Since you can't add or subtract final-ness
620 // of a field in a subclass, they can't share representations,
621 // and the amount of duplicated code is too small to warrant
622 // exposing a common abstract class.
623
624
625 /**
626 * An Entry maintaining a key and a value. The value may be
627 * changed using the <tt>setValue</tt> method. This class
628 * facilitates the process of building custom map
629 * implementations. For example, it may be convenient to return
630 * arrays of <tt>SimpleEntry</tt> instances in method
631 * <tt>Map.entrySet().toArray</tt>
632 */
633 public static class SimpleEntry<K,V> implements Entry<K,V> {
634 private final K key;
635 private V value;
636
637 /**
638 * Creates an entry representing a mapping from the specified
639 * key to the specified value.
640 *
641 * @param key the key represented by this entry
642 * @param value the value represented by this entry
643 */
644 public SimpleEntry(K key, V value) {
645 this.key = key;
646 this.value = value;
647 }
648
649 /**
650 * Creates an entry representing the same mapping as the
651 * specified entry.
652 *
653 * @param entry the entry to copy.
654 */
655 public SimpleEntry(Entry<? extends K, ? extends V> entry) {
656 this.key = entry.getKey();
657 this.value = entry.getValue();
658 }
659
660 /**
661 * Returns the key corresponding to this entry.
662 *
663 * @return the key corresponding to this entry.
664 */
665 public K getKey() {
666 return key;
667 }
668
669 /**
670 * Returns the value corresponding to this entry.
671 *
672 * @return the value corresponding to this entry.
673 */
674 public V getValue() {
675 return value;
676 }
677
678 /**
679 * Replaces the value corresponding to this entry with the specified
680 * value.
681 *
682 * @param value new value to be stored in this entry.
683 * @return the old value corresponding to the entry.
684 */
685 public V setValue(V value) {
686 V oldValue = this.value;
687 this.value = value;
688 return oldValue;
689 }
690
691 public boolean equals(Object o) {
692 if (!(o instanceof Map.Entry))
693 return false;
694 Map.Entry e = (Map.Entry)o;
695 return eq(key, e.getKey()) && eq(value, e.getValue());
696 }
697
698 public int hashCode() {
699 return ((key == null) ? 0 : key.hashCode()) ^
700 ((value == null) ? 0 : value.hashCode());
701 }
702
703 /**
704 * Returns a String representation of this map entry. This
705 * implementation returns the string representation of this
706 * entry's key followed by the equals character ("<tt>=</tt>")
707 * followed by the string representation of this entry's value.
708 *
709 * @return a String representation of this map entry.
710 */
711 public String toString() {
712 return key + "=" + value;
713 }
714
715 }
716
717 /**
718 * An Entry maintaining an immutable key and value, This class
719 * does not support method <tt>setValue</tt>. This class may be
720 * convenient in methods that return thread-safe snapshots of
721 * key-value mappings.
722 */
723 public static class SimpleImmutableEntry<K,V> implements Entry<K,V> {
724 private final K key;
725 private final V value;
726
727 /**
728 * Creates an entry representing a mapping from the specified
729 * key to the specified value.
730 *
731 * @param key the key represented by this entry
732 * @param value the value represented by this entry
733 */
734 public SimpleImmutableEntry(K key, V value) {
735 this.key = key;
736 this.value = value;
737 }
738
739 /**
740 * Creates an entry representing the same mapping as the
741 * specified entry.
742 *
743 * @param entry the entry to copy.
744 */
745 public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
746 this.key = entry.getKey();
747 this.value = entry.getValue();
748 }
749
750 /**
751 * Returns the key corresponding to this entry.
752 *
753 * @return the key corresponding to this entry.
754 */
755 public K getKey() {
756 return key;
757 }
758
759 /**
760 * Returns the value corresponding to this entry.
761 *
762 * @return the value corresponding to this entry.
763 */
764 public V getValue() {
765 return value;
766 }
767
768 /**
769 * Replaces the value corresponding to this entry with the specified
770 * value (optional operation). This implementation simply throws
771 * <tt>UnsupportedOperationException</tt>, as this class implements
772 * an <i>immutable</i> map entry.
773 *
774 * @param value new value to be stored in this entry.
775 * @return (Does not return)
776 * @throws UnsupportedOperationException always
777 */
778 public V setValue(V value) {
779 throw new UnsupportedOperationException();
780 }
781
782 public boolean equals(Object o) {
783 if (!(o instanceof Map.Entry))
784 return false;
785 Map.Entry e = (Map.Entry)o;
786 return eq(key, e.getKey()) && eq(value, e.getValue());
787 }
788
789 public int hashCode() {
790 return ((key == null) ? 0 : key.hashCode()) ^
791 ((value == null) ? 0 : value.hashCode());
792 }
793
794 /**
795 * Returns a String representation of this map entry. This
796 * implementation returns the string representation of this
797 * entry's key followed by the equals character ("<tt>=</tt>")
798 * followed by the string representation of this entry's value.
799 *
800 * @return a String representation of this map entry.
801 */
802 public String toString() {
803 return key + "=" + value;
804 }
805
806 }
807
808 }