--- jsr166/src/main/java/util/AbstractMap.java 2005/05/17 04:03:50 1.11 +++ jsr166/src/main/java/util/AbstractMap.java 2008/05/18 23:59:57 1.27 @@ -1,8 +1,26 @@ /* - * %W% %E% + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. - * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. */ package java.util; @@ -29,12 +47,12 @@ import java.util.Map.Entry; * constructor, as per the recommendation in the Map interface * specification. * - *

The documentation for each non-abstract methods in this class describes its + *

The documentation for each non-abstract method in this class describes its * implementation in detail. Each of these methods may be overridden if the * map being implemented admits a more efficient implementation. * *

This class is a member of the - * + * * Java Collections Framework. * * @param the type of keys maintained by this map @@ -42,7 +60,6 @@ import java.util.Map.Entry; * * @author Josh Bloch * @author Neal Gafter - * @version %I%, %G% * @see Map * @see Collection * @since 1.2 @@ -64,7 +81,7 @@ public abstract class AbstractMap i *

This implementation returns entrySet().size(). */ public int size() { - return entrySet().size(); + return entrySet().size(); } /** @@ -73,7 +90,7 @@ public abstract class AbstractMap i *

This implementation returns size() == 0. */ public boolean isEmpty() { - return size() == 0; + return size() == 0; } /** @@ -89,21 +106,21 @@ public abstract class AbstractMap i * @throws NullPointerException {@inheritDoc} */ public boolean containsValue(Object value) { - Iterator> i = entrySet().iterator(); - if (value==null) { - while (i.hasNext()) { - Entry e = i.next(); - if (e.getValue()==null) - return true; - } - } else { - while (i.hasNext()) { - Entry e = i.next(); - if (value.equals(e.getValue())) - return true; - } - } - return false; + Iterator> i = entrySet().iterator(); + if (value==null) { + while (i.hasNext()) { + Entry e = i.next(); + if (e.getValue()==null) + return true; + } + } else { + while (i.hasNext()) { + Entry e = i.next(); + if (value.equals(e.getValue())) + return true; + } + } + return false; } /** @@ -120,21 +137,21 @@ public abstract class AbstractMap i * @throws NullPointerException {@inheritDoc} */ public boolean containsKey(Object key) { - Iterator> i = entrySet().iterator(); - if (key==null) { - while (i.hasNext()) { - Entry e = i.next(); - if (e.getKey()==null) - return true; - } - } else { - while (i.hasNext()) { - Entry e = i.next(); - if (key.equals(e.getKey())) - return true; - } - } - return false; + Iterator> i = entrySet().iterator(); + if (key==null) { + while (i.hasNext()) { + Entry e = i.next(); + if (e.getKey()==null) + return true; + } + } else { + while (i.hasNext()) { + Entry e = i.next(); + if (key.equals(e.getKey())) + return true; + } + } + return false; } /** @@ -151,21 +168,21 @@ public abstract class AbstractMap i * @throws NullPointerException {@inheritDoc} */ public V get(Object key) { - Iterator> i = entrySet().iterator(); - if (key==null) { - while (i.hasNext()) { - Entry e = i.next(); - if (e.getKey()==null) - return e.getValue(); - } - } else { - while (i.hasNext()) { - Entry e = i.next(); - if (key.equals(e.getKey())) - return e.getValue(); - } - } - return null; + Iterator> i = entrySet().iterator(); + if (key==null) { + while (i.hasNext()) { + Entry e = i.next(); + if (e.getKey()==null) + return e.getValue(); + } + } else { + while (i.hasNext()) { + Entry e = i.next(); + if (key.equals(e.getKey())) + return e.getValue(); + } + } + return null; } @@ -183,7 +200,7 @@ public abstract class AbstractMap i * @throws IllegalArgumentException {@inheritDoc} */ public V put(K key, V value) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(); } /** @@ -208,28 +225,28 @@ public abstract class AbstractMap i * @throws NullPointerException {@inheritDoc} */ public V remove(Object key) { - Iterator> i = entrySet().iterator(); - Entry correctEntry = null; - if (key==null) { - while (correctEntry==null && i.hasNext()) { - Entry e = i.next(); - if (e.getKey()==null) - correctEntry = e; - } - } else { - while (correctEntry==null && i.hasNext()) { - Entry e = i.next(); - if (key.equals(e.getKey())) - correctEntry = e; - } - } - - V oldValue = null; - if (correctEntry !=null) { - oldValue = correctEntry.getValue(); - i.remove(); - } - return oldValue; + Iterator> i = entrySet().iterator(); + Entry correctEntry = null; + if (key==null) { + while (correctEntry==null && i.hasNext()) { + Entry e = i.next(); + if (e.getKey()==null) + correctEntry = e; + } + } else { + while (correctEntry==null && i.hasNext()) { + Entry e = i.next(); + if (key.equals(e.getKey())) + correctEntry = e; + } + } + + V oldValue = null; + if (correctEntry !=null) { + oldValue = correctEntry.getValue(); + i.remove(); + } + return oldValue; } @@ -252,11 +269,8 @@ public abstract class AbstractMap i * @throws IllegalArgumentException {@inheritDoc} */ public void putAll(Map m) { - Iterator> i = m.entrySet().iterator(); - while (i.hasNext()) { - Entry e = i.next(); - put(e.getKey(), e.getValue()); - } + for (Map.Entry e : m.entrySet()) + put(e.getKey(), e.getValue()); } /** @@ -271,7 +285,7 @@ public abstract class AbstractMap i * @throws UnsupportedOperationException {@inheritDoc} */ public void clear() { - entrySet().clear(); + entrySet().clear(); } @@ -301,36 +315,44 @@ public abstract class AbstractMap i * method will not all return the same set. */ public Set keySet() { - if (keySet == null) { - keySet = new AbstractSet() { - public Iterator iterator() { - return new Iterator() { - private Iterator> i = entrySet().iterator(); - - public boolean hasNext() { - return i.hasNext(); - } - - public K next() { - return i.next().getKey(); - } - - public void remove() { - i.remove(); - } + if (keySet == null) { + keySet = new AbstractSet() { + public Iterator iterator() { + return new Iterator() { + private Iterator> i = entrySet().iterator(); + + public boolean hasNext() { + return i.hasNext(); + } + + public K next() { + return i.next().getKey(); + } + + public void remove() { + i.remove(); + } }; - } + } + + public int size() { + return AbstractMap.this.size(); + } + + public boolean isEmpty() { + return AbstractMap.this.isEmpty(); + } + + public void clear() { + AbstractMap.this.clear(); + } - public int size() { - return AbstractMap.this.size(); - } - - public boolean contains(Object k) { - return AbstractMap.this.containsKey(k); - } - }; - } - return keySet; + public boolean contains(Object k) { + return AbstractMap.this.containsKey(k); + } + }; + } + return keySet; } /** @@ -349,36 +371,44 @@ public abstract class AbstractMap i * method will not all return the same collection. */ public Collection values() { - if (values == null) { - values = new AbstractCollection() { - public Iterator iterator() { - return new Iterator() { - private Iterator> i = entrySet().iterator(); - - public boolean hasNext() { - return i.hasNext(); - } - - public V next() { - return i.next().getValue(); - } - - public void remove() { - i.remove(); - } + if (values == null) { + values = new AbstractCollection() { + public Iterator iterator() { + return new Iterator() { + private Iterator> i = entrySet().iterator(); + + public boolean hasNext() { + return i.hasNext(); + } + + public V next() { + return i.next().getValue(); + } + + public void remove() { + i.remove(); + } }; } - public int size() { - return AbstractMap.this.size(); - } - - public boolean contains(Object v) { - return AbstractMap.this.containsValue(v); - } - }; - } - return values; + public int size() { + return AbstractMap.this.size(); + } + + public boolean isEmpty() { + return AbstractMap.this.isEmpty(); + } + + public void clear() { + AbstractMap.this.clear(); + } + + public boolean contains(Object v) { + return AbstractMap.this.containsValue(v); + } + }; + } + return values; } public abstract Set> entrySet(); @@ -387,7 +417,13 @@ public abstract class AbstractMap i // Comparison and hashing /** - * {@inheritDoc} + * Compares the specified object with this map for equality. Returns + * true if the given object is also a map and the two maps + * represent the same mappings. More formally, two maps m1 and + * m2 represent the same mappings if + * m1.entrySet().equals(m2.entrySet()). This ensures that the + * equals method works properly across different implementations + * of the Map interface. * *

This implementation first checks if the specified object is this map; * if so it returns true. Then, it checks if the specified @@ -397,28 +433,31 @@ public abstract class AbstractMap i * contains each mapping that this map contains. If the specified map * fails to contain such a mapping, false is returned. If the * iteration completes, true is returned. + * + * @param o object to be compared for equality with this map + * @return true if the specified object is equal to this map */ public boolean equals(Object o) { - if (o == this) - return true; + if (o == this) + return true; - if (!(o instanceof Map)) - return false; - Map t = (Map) o; - if (t.size() != size()) - return false; + if (!(o instanceof Map)) + return false; + Map m = (Map) o; + if (m.size() != size()) + return false; try { Iterator> i = entrySet().iterator(); while (i.hasNext()) { Entry e = i.next(); - K key = e.getKey(); + K key = e.getKey(); V value = e.getValue(); if (value == null) { - if (!(t.get(key)==null && t.containsKey(key))) + if (!(m.get(key)==null && m.containsKey(key))) return false; } else { - if (!value.equals(t.get(key))) + if (!value.equals(m.get(key))) return false; } } @@ -428,27 +467,32 @@ public abstract class AbstractMap i return false; } - return true; + return true; } /** - * {@inheritDoc} + * Returns the hash code value for this map. The hash code of a map is + * defined to be the sum of the hash codes of each entry in the map's + * entrySet() view. This ensures that m1.equals(m2) + * implies that m1.hashCode()==m2.hashCode() for any two maps + * m1 and m2, as required by the general contract of + * {@link Object#hashCode}. * *

This implementation iterates over entrySet(), calling - * hashCode() on each element (entry) in the set, and - * adding up the results. + * {@link Map.Entry#hashCode hashCode()} on each element (entry) in the + * set, and adding up the results. * + * @return the hash code value for this map * @see Map.Entry#hashCode() - * @see Object#hashCode() * @see Object#equals(Object) * @see Set#equals(Object) */ public int hashCode() { - int h = 0; - Iterator> i = entrySet().iterator(); - while (i.hasNext()) - h += i.next().hashCode(); - return h; + int h = 0; + Iterator> i = entrySet().iterator(); + while (i.hasNext()) + h += i.next().hashCode(); + return h; } /** @@ -459,43 +503,28 @@ public abstract class AbstractMap i * ", " (comma and space). Each key-value mapping is rendered as * the key followed by an equals sign ("=") followed by the * associated value. Keys and values are converted to strings as by - * String.valueOf(Object).

+ * {@link String#valueOf(Object)}. * - * This implementation creates an empty string buffer, appends a left - * brace, and iterates over the map's entrySet view, appending - * the string representation of each map.entry in turn. After - * appending each entry except the last, the string ", " is - * appended. Finally a right brace is appended. A string is obtained - * from the stringbuffer, and returned. - * - * @return a String representation of this map + * @return a string representation of this map */ public String toString() { - StringBuffer buf = new StringBuffer(); - buf.append("{"); - - Iterator> i = entrySet().iterator(); - boolean hasNext = i.hasNext(); - while (hasNext) { - Entry e = i.next(); - K key = e.getKey(); + Iterator> i = entrySet().iterator(); + if (! i.hasNext()) + return "{}"; + + StringBuilder sb = new StringBuilder(); + sb.append('{'); + for (;;) { + Entry e = i.next(); + K key = e.getKey(); V value = e.getValue(); - if (key == this) - buf.append("(this Map)"); - else - buf.append(key); - buf.append("="); - if (value == this) - buf.append("(this Map)"); - else - buf.append(value); - hasNext = i.hasNext(); - if (hasNext) - buf.append(", "); + sb.append(key == this ? "(this Map)" : key); + sb.append('='); + sb.append(value == this ? "(this Map)" : value); + if (! i.hasNext()) + return sb.append('}').toString(); + sb.append(", "); } - - buf.append("}"); - return buf.toString(); } /** @@ -516,7 +545,7 @@ public abstract class AbstractMap i * Test for equality, checking for nulls. */ private static boolean eq(Object o1, Object o2) { - return (o1 == null ? o2 == null : o1.equals(o2)); + return o1 == null ? o2 == null : o1.equals(o2); } // Implementation Note: SimpleEntry and SimpleImmutableEntry @@ -533,11 +562,17 @@ public abstract class AbstractMap i * facilitates the process of building custom map * implementations. For example, it may be convenient to return * arrays of SimpleEntry instances in method - * Map.entrySet().toArray + * Map.entrySet().toArray. + * + * @since 1.6 */ - public static class SimpleEntry implements Entry { - private final K key; - private V value; + public static class SimpleEntry + implements Entry, java.io.Serializable + { + private static final long serialVersionUID = -8499721149061103585L; + + private final K key; + private V value; /** * Creates an entry representing a mapping from the specified @@ -546,10 +581,10 @@ public abstract class AbstractMap i * @param key the key represented by this entry * @param value the value represented by this entry */ - public SimpleEntry(K key, V value) { - this.key = key; + public SimpleEntry(K key, V value) { + this.key = key; this.value = value; - } + } /** * Creates an entry representing the same mapping as the @@ -557,53 +592,87 @@ public abstract class AbstractMap i * * @param entry the entry to copy */ - public SimpleEntry(Entry entry) { - this.key = entry.getKey(); + public SimpleEntry(Entry entry) { + this.key = entry.getKey(); this.value = entry.getValue(); - } + } + + /** + * Returns the key corresponding to this entry. + * + * @return the key corresponding to this entry + */ + public K getKey() { + return key; + } + + /** + * Returns the value corresponding to this entry. + * + * @return the value corresponding to this entry + */ + public V getValue() { + return value; + } - /** - * Returns the key corresponding to this entry. - * - * @return the key corresponding to this entry - */ - public K getKey() { - return key; - } - - /** - * Returns the value corresponding to this entry. - * - * @return the value corresponding to this entry - */ - public V getValue() { - return value; - } - - /** - * Replaces the value corresponding to this entry with the specified - * value. - * - * @param value new value to be stored in this entry - * @return the old value corresponding to the entry - */ - public V setValue(V value) { - V oldValue = this.value; - this.value = value; - return oldValue; - } - - public boolean equals(Object o) { - if (!(o instanceof Map.Entry)) - return false; - Map.Entry e = (Map.Entry)o; - return eq(key, e.getKey()) && eq(value, e.getValue()); - } - - public int hashCode() { - return ((key == null) ? 0 : key.hashCode()) ^ - ((value == null) ? 0 : value.hashCode()); - } + /** + * Replaces the value corresponding to this entry with the specified + * value. + * + * @param value new value to be stored in this entry + * @return the old value corresponding to the entry + */ + public V setValue(V value) { + V oldValue = this.value; + this.value = value; + return oldValue; + } + + /** + * Compares the specified object with this entry for equality. + * Returns {@code true} if the given object is also a map entry and + * the two entries represent the same mapping. More formally, two + * entries {@code e1} and {@code e2} represent the same mapping + * if

+         *   (e1.getKey()==null ?
+         *    e2.getKey()==null :
+         *    e1.getKey().equals(e2.getKey()))
+         *   &&
+         *   (e1.getValue()==null ?
+         *    e2.getValue()==null :
+         *    e1.getValue().equals(e2.getValue()))
+ * This ensures that the {@code equals} method works properly across + * different implementations of the {@code Map.Entry} interface. + * + * @param o object to be compared for equality with this map entry + * @return {@code true} if the specified object is equal to this map + * entry + * @see #hashCode + */ + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry e = (Map.Entry)o; + return eq(key, e.getKey()) && eq(value, e.getValue()); + } + + /** + * Returns the hash code value for this map entry. The hash code + * of a map entry {@code e} is defined to be:
+         *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
+         *   (e.getValue()==null ? 0 : e.getValue().hashCode())
+ * This ensures that {@code e1.equals(e2)} implies that + * {@code e1.hashCode()==e2.hashCode()} for any two Entries + * {@code e1} and {@code e2}, as required by the general + * contract of {@link Object#hashCode}. + * + * @return the hash code value for this map entry + * @see #equals + */ + public int hashCode() { + return (key == null ? 0 : key.hashCode()) ^ + (value == null ? 0 : value.hashCode()); + } /** * Returns a String representation of this map entry. This @@ -613,21 +682,27 @@ public abstract class AbstractMap i * * @return a String representation of this map entry */ - public String toString() { - return key + "=" + value; - } + public String toString() { + return key + "=" + value; + } } /** - * An Entry maintaining an immutable key and value, This class + * An Entry maintaining an immutable key and value. This class * does not support method setValue. This class may be * convenient in methods that return thread-safe snapshots of * key-value mappings. + * + * @since 1.6 */ - public static class SimpleImmutableEntry implements Entry { - private final K key; - private final V value; + public static class SimpleImmutableEntry + implements Entry, java.io.Serializable + { + private static final long serialVersionUID = 7138329143949025153L; + + private final K key; + private final V value; /** * Creates an entry representing a mapping from the specified @@ -636,10 +711,10 @@ public abstract class AbstractMap i * @param key the key represented by this entry * @param value the value represented by this entry */ - public SimpleImmutableEntry(K key, V value) { - this.key = key; + public SimpleImmutableEntry(K key, V value) { + this.key = key; this.value = value; - } + } /** * Creates an entry representing the same mapping as the @@ -647,54 +722,88 @@ public abstract class AbstractMap i * * @param entry the entry to copy */ - public SimpleImmutableEntry(Entry entry) { - this.key = entry.getKey(); + public SimpleImmutableEntry(Entry entry) { + this.key = entry.getKey(); this.value = entry.getValue(); - } + } + + /** + * Returns the key corresponding to this entry. + * + * @return the key corresponding to this entry + */ + public K getKey() { + return key; + } + + /** + * Returns the value corresponding to this entry. + * + * @return the value corresponding to this entry + */ + public V getValue() { + return value; + } - /** - * Returns the key corresponding to this entry. - * - * @return the key corresponding to this entry - */ - public K getKey() { - return key; - } - - /** - * Returns the value corresponding to this entry. - * - * @return the value corresponding to this entry - */ - public V getValue() { - return value; - } - - /** - * Replaces the value corresponding to this entry with the specified - * value (optional operation). This implementation simply throws + /** + * Replaces the value corresponding to this entry with the specified + * value (optional operation). This implementation simply throws * UnsupportedOperationException, as this class implements * an immutable map entry. - * - * @param value new value to be stored in this entry - * @return (Does not return) - * @throws UnsupportedOperationException always + * + * @param value new value to be stored in this entry + * @return (Does not return) + * @throws UnsupportedOperationException always */ - public V setValue(V value) { + public V setValue(V value) { throw new UnsupportedOperationException(); } - public boolean equals(Object o) { - if (!(o instanceof Map.Entry)) - return false; - Map.Entry e = (Map.Entry)o; - return eq(key, e.getKey()) && eq(value, e.getValue()); - } - - public int hashCode() { - return ((key == null) ? 0 : key.hashCode()) ^ - ((value == null) ? 0 : value.hashCode()); - } + /** + * Compares the specified object with this entry for equality. + * Returns {@code true} if the given object is also a map entry and + * the two entries represent the same mapping. More formally, two + * entries {@code e1} and {@code e2} represent the same mapping + * if
+         *   (e1.getKey()==null ?
+         *    e2.getKey()==null :
+         *    e1.getKey().equals(e2.getKey()))
+         *   &&
+         *   (e1.getValue()==null ?
+         *    e2.getValue()==null :
+         *    e1.getValue().equals(e2.getValue()))
+ * This ensures that the {@code equals} method works properly across + * different implementations of the {@code Map.Entry} interface. + * + * @param o object to be compared for equality with this map entry + * @return {@code true} if the specified object is equal to this map + * entry + * @see #hashCode + */ + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry e = (Map.Entry)o; + return eq(key, e.getKey()) && eq(value, e.getValue()); + } + + /** + * Returns the hash code value for this map entry. The hash code + * of a map entry {@code e} is defined to be:
+         *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
+         *   (e.getValue()==null ? 0 : e.getValue().hashCode())
+ * This ensures that {@code e1.equals(e2)} implies that + * {@code e1.hashCode()==e2.hashCode()} for any two Entries + * {@code e1} and {@code e2}, as required by the general + * contract of {@link Object#hashCode}. + * + * @return the hash code value for this map entry + * @see #equals + */ + public int hashCode() { + return (key == null ? 0 : key.hashCode()) ^ + (value == null ? 0 : value.hashCode()); + } /** * Returns a String representation of this map entry. This @@ -704,9 +813,9 @@ public abstract class AbstractMap i * * @return a String representation of this map entry */ - public String toString() { - return key + "=" + value; - } + public String toString() { + return key + "=" + value; + } }