KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jac > util > AbstractMap


1 /*
2  * @(#)AbstractMap.java 1.32 01/12/03
3  *
4  * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package org.objectweb.jac.util;
9
10 import java.util.AbstractSet JavaDoc;
11 import java.util.Collection JavaDoc;
12 import java.util.Iterator JavaDoc;
13 import java.util.Map.Entry;
14 import java.util.Map JavaDoc;
15 import java.util.Set JavaDoc;
16 import java.util.AbstractCollection JavaDoc;
17
18 /**
19  * This class provides a skeletal implementation of the <tt>Map</tt>
20  * interface, to minimize the effort required to implement this interface. <p>
21  *
22  * To implement an unmodifiable map, the programmer needs only to extend this
23  * class and provide an implementation for the <tt>entrySet</tt> method, which
24  * returns a set-view of the map's mappings. Typically, the returned set
25  * will, in turn, be implemented atop <tt>AbstractSet</tt>. This set should
26  * not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator
27  * should not support the <tt>remove</tt> method.<p>
28  *
29  * To implement a modifiable map, the programmer must additionally override
30  * this class's <tt>put</tt> method (which otherwise throws an
31  * <tt>UnsupportedOperationException</tt>), and the iterator returned by
32  * <tt>entrySet().iterator()</tt> must additionally implement its
33  * <tt>remove</tt> method.<p>
34  *
35  * The programmer should generally provide a void (no argument) and map
36  * constructor, as per the recommendation in the <tt>Map</tt> interface
37  * specification.<p>
38  *
39  * The documentation for each non-abstract methods in this class describes its
40  * implementation in detail. Each of these methods may be overridden if the
41  * map being implemented admits a more efficient implementation.
42  *
43  * @author Josh Bloch
44  * @version 1.32, 12/03/01
45  * @see Map
46  * @see Collection
47  * @since 1.2
48  */

49
50 public abstract class AbstractMap implements Map JavaDoc {
51     /**
52      * Sole constructor. (For invocation by subclass constructors, typically
53      * implicit.)
54      */

55     protected AbstractMap() {
56     }
57
58     // Query Operations
59

60     /**
61      * Returns the number of key-value mappings in this map. If the map
62      * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
63      * <tt>Integer.MAX_VALUE</tt>.<p>
64      *
65      * This implementation returns <tt>entrySet().size()</tt>.
66      *
67      * @return the number of key-value mappings in this map.
68      */

69     public int size() {
70         return entrySet().size();
71     }
72
73     /**
74      * Returns <tt>true</tt> if this map contains no key-value mappings. <p>
75      *
76      * This implementation returns <tt>size() == 0</tt>.
77      *
78      * @return <tt>true</tt> if this map contains no key-value mappings.
79      */

80     public boolean isEmpty() {
81         return size() == 0;
82     }
83
84     /**
85      * Returns <tt>true</tt> if this map maps one or more keys to this value.
86      * More formally, returns <tt>true</tt> if and only if this map contains
87      * at least one mapping to a value <tt>v</tt> such that <tt>(value==null ?
88      * v==null : value.equals(v))</tt>. This operation will probably require
89      * time linear in the map size for most implementations of map.<p>
90      *
91      * This implementation iterates over entrySet() searching for an entry
92      * with the specified value. If such an entry is found, <tt>true</tt> is
93      * returned. If the iteration terminates without finding such an entry,
94      * <tt>false</tt> is returned. Note that this implementation requires
95      * linear time in the size of the map.
96      *
97      * @param value value whose presence in this map is to be tested.
98      *
99      * @return <tt>true</tt> if this map maps one or more keys to this value.
100      */

101     public boolean containsValue(Object JavaDoc value) {
102         Iterator JavaDoc 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 JavaDoc key) {
138         Iterator JavaDoc 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 JavaDoc get(Object JavaDoc key) {
179         Iterator JavaDoc 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 JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
230         throw new UnsupportedOperationException JavaDoc();
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 JavaDoc remove(Object JavaDoc key) {
261         Iterator JavaDoc 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 JavaDoc 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 JavaDoc t) {
316         Iterator JavaDoc 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 JavaDoc keySet = null;
348     transient volatile Collection JavaDoc 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 JavaDoc keySet() {
374         if (keySet == null) {
375             keySet = new AbstractSet JavaDoc() {
376                     public Iterator JavaDoc iterator() {
377                         return new Iterator JavaDoc() {
378                                 private Iterator JavaDoc i = entrySet().iterator();
379
380                                 public boolean hasNext() {
381                                     return i.hasNext();
382                                 }
383
384                                 public Object JavaDoc 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 JavaDoc 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 JavaDoc values() {
431         if (values == null) {
432             values = new AbstractCollection JavaDoc() {
433                     public Iterator JavaDoc iterator() {
434                         return new Iterator JavaDoc() {
435                                 private Iterator JavaDoc i = entrySet().iterator();
436
437                                 public boolean hasNext() {
438                                     return i.hasNext();
439                                 }
440
441                                 public Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc o) {
505         if (o == this)
506             return true;
507
508         if (!(o instanceof Map JavaDoc))
509             return false;
510         Map JavaDoc t = (Map JavaDoc) o;
511         if (t.size() != size())
512             return false;
513
514         try {
515             Iterator JavaDoc i = entrySet().iterator();
516             while (i.hasNext()) {
517                 Entry e = (Entry) i.next();
518                 Object JavaDoc key = e.getKey();
519                 Object JavaDoc 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 JavaDoc unused) {
529             return false;
530         } catch(NullPointerException JavaDoc 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 JavaDoc 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 JavaDoc toString() {
583         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
584         buf.append("{");
585
586         Iterator JavaDoc i = entrySet().iterator();
587         boolean hasNext = i.hasNext();
588         while (hasNext) {
589             Entry e = (Entry) (i.next());
590             Object JavaDoc key = e.getKey();
591             Object JavaDoc 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 JavaDoc clone() throws CloneNotSupportedException JavaDoc {
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 JavaDoc key;
623         Object JavaDoc value;
624
625         public SimpleEntry(Object JavaDoc key, Object JavaDoc value) {
626             this.key = key;
627             this.value = value;
628         }
629
630         public SimpleEntry(Map.Entry JavaDoc e) {
631             this.key = e.getKey();
632             this.value = e.getValue();
633         }
634
635         public Object JavaDoc getKey() {
636             return key;
637         }
638
639         public Object JavaDoc getValue() {
640             return value;
641         }
642
643         public Object JavaDoc setValue(Object JavaDoc value) {
644             Object JavaDoc oldValue = this.value;
645             this.value = value;
646             return oldValue;
647         }
648
649         public boolean equals(Object JavaDoc o) {
650             if (!(o instanceof Map.Entry JavaDoc))
651                 return false;
652             Map.Entry JavaDoc e = (Map.Entry JavaDoc)o;
653             return eq(key, e.getKey()) && eq(value, e.getValue());
654         }
655
656         public int hashCode() {
657             Object JavaDoc v;
658             return ((key == null) ? 0 : key.hashCode()) ^
659                 ((value == null) ? 0 : value.hashCode());
660         }
661
662         public String JavaDoc toString() {
663             return key + "=" + value;
664         }
665
666         private static boolean eq(Object JavaDoc o1, Object JavaDoc o2) {
667             return (o1 == null ? o2 == null : o1.equals(o2));
668         }
669     }
670 }
671
Popular Tags