/[classpath]/classpath/java/util/AbstractMap.java
ViewVC logotype

Diff of /classpath/java/util/AbstractMap.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.15 by cbj, Fri Aug 31 23:19:09 2001 UTC revision 1.16 by ericb, Fri Oct 19 00:17:44 2001 UTC
# Line 1  Line 1 
1  /* AbstractMap.java -- Abstract implementation of most of Map  /* AbstractMap.java -- Abstract implementation of most of Map
2     Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3    
4  This file is part of GNU Classpath.  This file is part of GNU Classpath.
5    
# Line 25  This exception does not however invalida Line 25  This exception does not however invalida
25  executable file might be covered by the GNU General Public License. */  executable file might be covered by the GNU General Public License. */
26    
27    
 // TO DO:  
 // comments  
 // test suite  
   
28  package java.util;  package java.util;
29    
30    /**
31     * An abstract implementation of Map to make it easier to create your own
32     * implementations. In order to create an unmodifiable Map, subclass
33     * AbstractMap and implement the <code>entrySet</code> (usually via an
34     * AbstractSet).  To make it modifiable, also implement <code>put</code>,
35     * and have <code>entrySet().iterator()</code> support <code>remove</code>.
36     * <p>
37     *
38     * It is recommended that classes which extend this support at least the
39     * no-argument constructor, and a constructor which accepts another Map.
40     * Further methods in this class may be overridden if you have a more
41     * efficient implementation.
42     *
43     * @author Original author unknown
44     * @author Eric Blake <ebb9@email.byu.edu>
45     * @see Map
46     * @see Collection
47     * @see HashMap
48     * @see LinkedHashMap
49     * @see TreeMap
50     * @see WeakHashMap
51     * @see IdentityHashMap
52     * @since 1.2
53     * @status updated to 1.4
54     */
55  public abstract class AbstractMap implements Map  public abstract class AbstractMap implements Map
56  {  {
57      /** An "enum" of iterator types. */
58      // Package visible for use by subclasses.
59      static final int KEYS = 0,
60                       VALUES = 1,
61                       ENTRIES = 2;
62    
63      /**
64       * The cache for {@link #keySet()}.
65       */
66      // Package visible for use by subclasses.
67      Set keys;
68    
69      /**
70       * The cache for {@link #values()}.
71       */
72      // Package visible for use by subclasses.
73      Collection values;
74    
75    /**    /**
76     * Remove all entries from this Map. This default implementation calls     * The main constructor, for use by subclasses.
77     * entrySet().clear().     */
78      protected AbstractMap()
79      {
80      }
81    
82      /**
83       * Remove all entries from this Map (optional operation). This default
84       * implementation calls entrySet().clear(). NOTE: If the entry set does
85       * not permit clearing, then this will fail, too. Subclasses often
86       * override this for efficiency.  Your implementation of entrySet() should
87       * not call <code>AbstractMap.clear</code> unless you want an infinite loop.
88     *     *
89     * @throws UnsupportedOperationException     * @throws UnsupportedOperationException if <code>entrySet().clear()</code>
90     * @specnote The JCL book claims that this implementation always throws     *         does not support clearing.
91     *           UnsupportedOperationException, while the online docs claim it     * @see Set#clear()
    *           calls entrySet().clear(). We take the later to be correct.  
92     */     */
93    public void clear()    public void clear()
94    {    {
# Line 48  public abstract class AbstractMap implem Line 96  public abstract class AbstractMap implem
96    }    }
97    
98    /**    /**
99     * Create a shallow copy of this Map, no keys or values are copied.     * Create a shallow copy of this Map, no keys or values are copied. The
100       * default implementation simply calls <code>super.clone()</code>.
101       *
102       * @return the shallow clone
103       * @throws CloneNotSupportedException if a subclass is not Cloneable
104       * @see Cloneable
105       * @see Object#clone()
106     */     */
107    protected Object clone () throws CloneNotSupportedException    protected Object clone() throws CloneNotSupportedException
108    {    {
109      return super.clone ();      AbstractMap copy = (AbstractMap) super.clone();
110        // Clear out the caches; they are stale.
111        copy.keys = null;
112        copy.values = null;
113        return copy;
114    }    }
115    
116      /**
117       * Returns true if this contains a mapping for the given key. This
118       * implementation does a linear search, O(n), over the
119       * <code>entrySet()</code>, returning <code>true</code> if a match
120       * is found, <code>false</code> if the iteration ends. Many subclasses
121       * can implement this more efficiently.
122       *
123       * @param key the key to search for
124       * @return true if the map contains the key
125       * @throws NullPointerException if key is <code>null</code> but the map
126       *         does not permit null keys
127       * @see #containsValue(Object)
128       */
129    public boolean containsKey(Object key)    public boolean containsKey(Object key)
130    {    {
131      Object k;      Iterator entries = entrySet().iterator();
132      Set es = entrySet();      int pos = size();
133      Iterator entries = es.iterator();      while (--pos >= 0)
134      int size = size();        if (equals(key, ((Map.Entry) entries.next()).getKey()))
135      for (int pos = 0; pos < size; pos++)          return true;
       {  
         k = ((Map.Entry) entries.next()).getKey();  
         if (key == null ? k == null : key.equals(k))  
           return true;  
       }  
136      return false;      return false;
137    }    }
138    
139      /**
140       * Returns true if this contains at least one mapping with the given value.
141       * This implementation does a linear search, O(n), over the
142       * <code>entrySet()</code>, returning <code>true</code> if a match
143       * is found, <code>false</code> if the iteration ends. A match is
144       * defined as <code>(value == null ? v == null : value.equals(v))</code>
145       * Subclasses are unlikely to implement this more efficiently.
146       *
147       * @param value the value to search for
148       * @return true if the map contains the value
149       * @see #containsKey(Object)
150       */
151    public boolean containsValue(Object value)    public boolean containsValue(Object value)
152    {    {
153      Object v;      Iterator entries = entrySet().iterator();
154      Set es = entrySet();      int pos = size();
155      Iterator entries = es.iterator();      while (--pos >= 0)
156      int size = size();        if (equals(value, ((Map.Entry) entries.next()).getValue()))
157      for (int pos = 0; pos < size; pos++)          return true;
       {  
         v = ((Map.Entry) entries.next()).getValue();  
         if (value == null ? v == null : value.equals(v))  
           return true;  
       }  
158      return false;      return false;
159    }    }
160    
161      /**
162       * Returns a set view of the mappings in this Map.  Each element in the
163       * set must be an implementation of Map.Entry.  The set is backed by
164       * the map, so that changes in one show up in the other.  Modifications
165       * made while an iterator is in progress cause undefined behavior.  If
166       * the set supports removal, these methods must be valid:
167       * <code>Iterator.remove</code>, <code>Set.remove</code>,
168       * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>.
169       * Element addition is not supported via this set.
170       *
171       * @return the entry set
172       * @see Map.Entry
173       */
174    public abstract Set entrySet();    public abstract Set entrySet();
175    
176      /**
177       * Compares the specified object with this map for equality. Returns
178       * <code>true</code> if the other object is a Map with the same mappings,
179       * that is,<br>
180       * <code>o instanceof Map && entrySet().equals(((Map) o).entrySet();</code>
181       *
182       * @param o the object to be compared
183       * @return true if the object equals this map
184       * @see Set#equals(Object)
185       */
186    public boolean equals(Object o)    public boolean equals(Object o)
187    {    {
188      if (o == this)      return (o == this ||
189        return true;              (o instanceof Map &&
190      if (!(o instanceof Map))               entrySet().equals(((Map) o).entrySet())));
       return false;  
   
     Map m = (Map) o;  
     Set s = m.entrySet();  
     Iterator itr = entrySet().iterator();  
     int size = size();  
   
     if (m.size() != size)  
       return false;  
   
     for (int pos = 0; pos < size; pos++)  
       {  
         if (!s.contains(itr.next()))  
           return false;  
       }  
     return true;  
191    }    }
192    
193      /**
194       * Returns the value mapped by the given key. Returns <code>null</code> if
195       * there is no mapping.  However, in Maps that accept null values, you
196       * must rely on <code>containsKey</code> to determine if a mapping exists.
197       * This iteration takes linear time, searching entrySet().iterator() of
198       * the key.  Many implementations override this method.
199       *
200       * @param key the key to look up
201       * @return the value associated with the key, or null if key not in map
202       * @throws NullPointerException if this map does not accept null keys
203       * @see #containsKey(Object)
204       */
205    public Object get(Object key)    public Object get(Object key)
206    {    {
207      Set s = entrySet();      Iterator entries = entrySet().iterator();
208      Iterator entries = s.iterator();      int pos = size();
209      int size = size();      while (--pos >= 0)
   
     for (int pos = 0; pos < size; pos++)  
210        {        {
211          Map.Entry entry = (Map.Entry) entries.next();          Map.Entry entry = (Map.Entry) entries.next();
212          Object k = entry.getKey();          Object k = entry.getKey();
213          if (key == null ? k == null : key.equals(k))          if (equals(key, k))
214            return entry.getValue();            return entry.getValue();
215        }        }
   
216      return null;      return null;
217    }    }
218    
219      /**
220       * Returns the hash code for this map. As defined in Map, this is the sum
221       * of all hashcodes for each Map.Entry object in entrySet.
222       *
223       * @return the hash code
224       * @see Map.Entry#hashCode()
225       * @see Set#hashCode()
226       */
227    public int hashCode()    public int hashCode()
228    {    {
229      int hashcode = 0;      int hashcode = 0;
230      Iterator itr = entrySet().iterator();      Iterator itr = entrySet().iterator();
231      int size = size();      int pos = size();
232      for (int pos = 0; pos < size; pos++)      while (--pos >= 0)
233        {        hashcode += itr.next().hashCode();
         hashcode += itr.next().hashCode();  
       }  
234      return hashcode;      return hashcode;
235    }    }
236    
237      /**
238       * Returns true if the map contains no mappings. This is implemented by
239       * <code>size() == 0</code>.
240       *
241       * @return true if the map is empty
242       * @see #size()
243       */
244    public boolean isEmpty()    public boolean isEmpty()
245    {    {
246      return size() == 0;      return size() == 0;
247    }    }
248    
249      /**
250       * Returns a set view of this map's keys. The set is backed by the map,
251       * so changes in one show up in the other. Modifications while an iteration
252       * is in progress produce undefined behavior. The set supports removal
253       * if entrySet() does, but does not support element addition.
254       * <p>
255       *
256       * This implementation creates an AbstractSet, where the iterator wraps
257       * the entrySet iterator, size defers to the Map's size, and contains
258       * defers to the Map's containsKey. The set is created on first use, and
259       * returned on subsequent uses, although since no synchronization occurs,
260       * there is a slight possibility of creating two sets.
261       *
262       * @return a Set view of the keys
263       * @see Set#iterator()
264       * @see #size()
265       * @see #containsKey(Object)
266       * @see #values()
267       */
268    public Set keySet()    public Set keySet()
269    {    {
270      if (this.keySet == null)      if (keys == null)
271          keys = new AbstractSet()
272        {        {
273          this.keySet = new AbstractSet()          public int size()
274          {          {
275            public int size()            return AbstractMap.this.size();
276            {          }
277              return AbstractMap.this.size();  
278            }          public boolean contains(Object key)
279            {
280            public boolean contains(Object key)            return containsKey(key);
281            {          }
282              return AbstractMap.this.containsKey(key);  
283            }          public Iterator iterator()
284            {
285            public Iterator iterator()            return new Iterator()
286            {            {
287              return new Iterator()              private final Iterator map_iterator = entrySet().iterator();
288              {  
289                Iterator map_iterator = AbstractMap.this.entrySet().iterator();              public boolean hasNext()
290                {
291                public boolean hasNext()                return map_iterator.hasNext();
292                {              }
293                  return map_iterator.hasNext();  
294                }              public Object next()
295                {
296                public Object next()                return ((Map.Entry) map_iterator.next()).getKey();
297                {              }
298                  return ((Map.Entry) map_iterator.next()).getKey();  
299                }              public void remove()
300                {
301                public void remove()                map_iterator.remove();
302                {              }
303                  map_iterator.remove();            };
304                }          }
305              };        };
306            }      return keys;
         };  
       }  
   
     return this.keySet;  
307    }    }
308    
309      /**
310       * Associates the given key to the given value (optional operation). If the
311       * map already contains the key, its value is replaced. This implementation
312       * simply throws an UnsupportedOperationException. Be aware that in a map
313       * that permits <code>null</code> values, a null return does not always
314       * imply that the mapping was created.
315       *
316       * @param key the key to map
317       * @param value the value to be mapped
318       * @return the previous value of the key, or null if there was no mapping
319       * @throws UnsupportedOperationException if the operation is not supported
320       * @throws ClassCastException if the key or value is of the wrong type
321       * @throws IllegalArgumentException if something about this key or value
322       *         prevents it from existing in this map
323       * @throws NullPointerException if the map forbids null keys or values
324       * @see #containsKey(Object)
325       */
326    public Object put(Object key, Object value)    public Object put(Object key, Object value)
327    {    {
328      throw new UnsupportedOperationException();      throw new UnsupportedOperationException();
329    }    }
330    
331      /**
332       * Copies all entries of the given map to this one (optional operation). If
333       * the map already contains a key, its value is replaced. This implementation
334       * simply iterates over the map's entrySet(), calling <code>put</code>,
335       * so it is not supported if puts are not.
336       *
337       * @param m the mapping to load into this map
338       * @throws UnsupportedOperationException if the operation is not supported
339       * @throws ClassCastException if a key or value is of the wrong type
340       * @throws IllegalArgumentException if something about a key or value
341       *         prevents it from existing in this map
342       * @throws NullPointerException if the map forbids null keys or values, or
343       *         if <code>m</code> is null.
344       * @see #put(Object, Object)
345       */
346    public void putAll(Map m)    public void putAll(Map m)
347    {    {
     Map.Entry entry;  
348      Iterator entries = m.entrySet().iterator();      Iterator entries = m.entrySet().iterator();
349      int size = m.size();      int pos = size();
350        while (--pos >= 0)
     for (int pos = 0; pos < size; pos++)  
351        {        {
352          entry = (Map.Entry) entries.next();          Map.Entry entry = (Map.Entry) entries.next();
353          put(entry.getKey(), entry.getValue());          put(entry.getKey(), entry.getValue());
354        }        }
355    }    }
356    
357      /**
358       * Removes the mapping for this key if present (optional operation). This
359       * implementation iterates over the entrySet searching for a matching
360       * key, at which point it calls the iterator's <code>remove</code> method.
361       * It returns the result of <code>getValue()</code> on the entry, if found,
362       * or null if no entry is found. Note that maps which permit null values
363       * may also return null if the key was removed.  If the entrySet does not
364       * support removal, this will also fail. This is O(n), so many
365       * implementations override it for efficiency.
366       *
367       * @param key the key to remove
368       * @return the value the key mapped to, or null if not present
369       * @throws UnsupportedOperationException if deletion is unsupported
370       * @see Iterator#remove()
371       */
372    public Object remove(Object key)    public Object remove(Object key)
373    {    {
374      Iterator entries = entrySet().iterator();      Iterator entries = entrySet().iterator();
375      int size = size();      int pos = size();
376        while (--pos >= 0)
     for (int pos = 0; pos < size; pos++)  
377        {        {
378          Map.Entry entry = (Map.Entry) entries.next();          Map.Entry entry = (Map.Entry) entries.next();
379          Object k = entry.getKey();          Object k = entry.getKey();
380          if (key == null ? k == null : key.equals(k))          if (equals(key, k))
381            {            {
382              Object value = entry.getValue();              // Must get the value before we remove it from iterator.
383              entries.remove();              Object r = entry.getValue();
384              return value;              entries.remove();
385            }              return r;
386              }
387        }        }
   
388      return null;      return null;
389    }    }
390    
391      /**
392       * Returns the number of key-value mappings in the map. If there are more
393       * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. This is
394       * implemented as <code>entrySet().size()</code>.
395       *
396       * @return the number of mappings
397       * @see Set#size()
398       */
399    public int size()    public int size()
400    {    {
401      return entrySet().size();      return entrySet().size();
402    }    }
403    
404      /**
405       * Returns a String representation of this map. This is a listing of the
406       * map entries (which are specified in Map.Entry as being
407       * <code>getKey() + "=" + getValue()</code>), separated by a comma and
408       * space (", "), and surrounded by braces ('{' and '}'). This implementation
409       * uses a StringBuffer and iterates over the entrySet to build the String.
410       * Note that this can fail with an exception if underlying keys or
411       * values complete abruptly in toString().
412       *
413       * @return a String representation
414       * @see Map.Entry#toString()
415       */
416    public String toString()    public String toString()
417    {    {
418      Iterator entries = entrySet().iterator();      Iterator entries = entrySet().iterator();
     int size = size();  
419      StringBuffer r = new StringBuffer("{");      StringBuffer r = new StringBuffer("{");
420      for (int pos = 0; pos < size; pos++)      for (int pos = size(); pos > 0; pos--)
421        {        {
422          // Append the toString value of the entries rather than calling          // Append the toString value of the entries rather than calling
423          // getKey/getValue. This is more efficient and it matches the JDK          // getKey/getValue. This is more efficient and it matches the JDK
424          // behaviour.          // behaviour.
425          r.append(entries.next());                r.append(entries.next());
426          if (pos < size - 1)          if (pos > 1)
427            r.append(", ");            r.append(", ");
428        }        }
429      r.append("}");      r.append("}");
430      return r.toString();      return r.toString();
431    }    }
432    
433      /**
434       * Returns a collection or bag view of this map's values. The collection
435       * is backed by the map, so changes in one show up in the other.
436       * Modifications while an iteration is in progress produce undefined
437       * behavior. The collection supports removal if entrySet() does, but
438       * does not support element addition.
439       * <p>
440       *
441       * This implementation creates an AbstractCollection, where the iterator
442       * wraps the entrySet iterator, size defers to the Map's size, and contains
443       * defers to the Map's containsValue. The collection is created on first
444       * use, and returned on subsequent uses, although since no synchronization
445       * occurs, there is a slight possibility of creating two collections.
446       *
447       * @return a Collection view of the values
448       * @see Collection#iterator()
449       * @see #size()
450       * @see #containsValue(Object)
451       * @see #keySet()
452       */
453    public Collection values()    public Collection values()
454    {    {
455      if (this.valueCollection == null)      if (values == null)
456          values = new AbstractCollection()
457        {        {
458          this.valueCollection = new AbstractCollection()          public int size()
459          {          {
460            public int size()            return AbstractMap.this.size();
461            {          }
462              return AbstractMap.this.size();  
463            }          public Iterator iterator()
464            {
465            public Iterator iterator()            return new Iterator()
466            {            {
467              return new Iterator()              Iterator map_iterator = entrySet().iterator();
468              {  
469                Iterator map_iterator = AbstractMap.this.entrySet().iterator();              public boolean hasNext()
470                {
471                public boolean hasNext()                return map_iterator.hasNext();
472                {              }
473                  return map_iterator.hasNext();  
474                }              public Object next()
475                {
476                public Object next()                return ((Map.Entry) map_iterator.next()).getValue();
477                {              }
478                  return ((Map.Entry) map_iterator.next()).getValue();  
479                }              public void remove()
480                {
481                public void remove()                map_iterator.remove();
482                {              }
483                  map_iterator.remove();            };
484                }          }
485              };        };
486            }      return values;
487          };    }
       }  
488    
489      return this.valueCollection;    /**
490       * Compare two objects according to Collection semantics.
491       *
492       * @param o1 the first object
493       * @param o2 the second object
494       * @return o1 == null ? o2 == null : o1.equals(o2)
495       */
496      // Package visible for use throughout java.util.
497      // It may be inlined since it is final.
498      static final boolean equals(Object o1, Object o2)
499      {
500        return o1 == null ? o2 == null : o1.equals(o2);
501    }    }
502    
503    private Collection valueCollection = null;    /**
504    private Set keySet = null;     * Hash an object according to Collection semantics.
505       *
506       * @param o the object to hash
507       * @return o1 == null ? 0 : o1.hashCode()
508       */
509      // Package visible for use throughout java.util.
510      // It may be inlined since it is final.
511      static final int hashCode(Object o)
512      {
513        return o == null ? 0 : o.hashCode();
514      }
515  }  }

Legend:
Removed from v.1.15  
changed lines
  Added in v.1.16

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26