]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Collections drop from Classpath:
authorBryce McKinlay <bryce@gcc.gnu.org>
Sat, 15 Dec 2001 07:47:03 +0000 (07:47 +0000)
committerBryce McKinlay <bryce@gcc.gnu.org>
Sat, 15 Dec 2001 07:47:03 +0000 (07:47 +0000)
2001-12-15  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>

* java/util/BitSet.java (and): Fix off-by-one bug, don't skip part of
the bitset.
(andNot): Likewise.
(xor): Likewise.

2001-12-15  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>

* java/util/LinkedList.java (LinkedListItr.add): Don't skip the next
entry.

2001-12-15  Eric Blake  <ebb9@email.byu.edu>

* java/util/TreeMap.java (removeNode): Fix bug in node removal.

2001-12-15  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>

* java/util/AbstractCollection.java (containsAll): Use size of the
correct collection for loop bound.
* java/util/AbstractList.java (iterator.next): Increment pos after
calling get on backing list.
(listIterator.next): Likewise.
* java/util/LinkedList.java (addLastEntry): Don't increment size before
checking for size == 0.
(addFirstEntry): Rearrange to match addLastEntry.
(add): Do not increment size before inserting the new entry.

* java/util/AbstractCollection.java (addAll): Use size of the
correct collection for loop bound.

2001-12-15  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>

* java/util/AbstractSet.java (removeAll): Fix scoping thinko.
* java/util/HashMap.java (putAllInternal): Set size here.
* java/util/Hashtable.java (putAllInternal): New method. Copy contents
of a map efficiently without calling put() or putAll().
(Hashtable (map)): Use putAllInternal.
(clone): Likewise.

2001-12-15  Eric Blake  <ebb9@email.byu.edu>

* java/util/Collections.java:
* java/util/Vector.java:
* java/util/WeakHashMap.java: Fix spelling errors.

2001-12-15  Eric Blake  <ebb9@email.byu.edu>

* java/util/AbstractCollection.java (removeAllInternal),
(retainAllInternal): Add hooks for use by ArrayList.
* java/util/AbstractList.java: Minor code updates. Fix some
scoping.
* java/util/AbstractMap.java: ditto
* java/util/ArrayList.java (readObject, writeObject): ditto
(removeAllInternal, retainAllInternal): Optimize.
* java/util/Arrays.java: ditto
* java/util/Collections.java: ditto. Change order of parameters
to equals(Object, Object) to match specs.
* java/util/Dictionary.java: Improve javadoc.
(Dictionary): Add explicit constructor.
* java/util/HashMap.java: Improve javadoc. Rearrange methods to
follow order in JDK. Cleanups related to recent code migration to
AbstractMap. Fix some scoping.
(entrySet): Cache the result.
(modCount): Ensure that this is updated correctly.
* java/util/HashSet.java: Improve javadoc. Fix some scoping.
(init): Add hooks for LinkedHashSet.
(map): Use "" instead of Boolean.TRUE in backing map. Use
package-private API where possible for less overhead.
(readObject, writeObject): Fix serialization.
* java/util/Hashtable.java: Improve javadoc. Fix some scoping.
(entrySet, keySet, values): Cache the result.
(modCount): Ensure that this is updated correctly.
(contains, remove): Fix NullPointer checking to match specs.
(class Enumeration): Make more like HashIterator.
* java/util/IdentityHashMap.java: Minor code updates.
(modCount): Ensure that this is updated correctly.
(readObject, writeObject): Fix serialization.
* java/util/LinkedHashMap.java: Minor code updates. Cleanups
related to recent code migration to AbstractMap.
* java/util/LinkedHashSet.java: New file.
* java/util/LinkedList.java:
(readObject, writeObject): Fix serialization.
* java/util/Makefile.am: List recently added files.
* java/util/Stack.java: Minor code updates.
* java/util/TreeMap.java: Improve javadoc. Overhaul the class to
be more efficient. Fix some scoping. Rearrange the methods.
(nil): Ensure that this can be thread-safe, and make it a static
final. Initialize it to be more useful as a sentinal node.
(Node): Specify color in constructor.
(deleteFixup, insertFixup): Improve comments and algorithm.
(fabricateTree): Redesign with less overhead.
(lowestGreaterThan): Add parameter first to make SubMap easier.
(removeNode): Patch hole where nil was being modified. Choose
predecessor instead of successor so in-place swap works.
(class VerifyResult, verifyTree, verifySub, verifyError): Remove
this dead code after verifying the class works.
(class SubMap): Rewrite several algorithms to avoid problems with
comparing nil.
* java/util/TreeSet.java: Improve javadoc. Fix some scoping.
(clone): Fix ClassCastException when cloning subSet().
(readObject, writeObject): Fix serialization.
* java/util/WeakHashMap.java: Improve javadoc. Fix some scoping.
(NULL_KEY): Make it compare as null, for ease elsewhere.
(Class WeakEntry): Rename from Entry, to avoid shadowing
Map.Entry. Add missing toString.
(modCount): Ensure that this is updated correctly.
(clear, containsValue, keySet, putAll, values, WeakHashMap(Map)):
Add missing methods and constructor.

2001-12-15  Eric Blake  <ebb9@email.byu.edu>

* java/util/ArrayList.java (checkBoundExclusive),
(checkBoundInclusive): Rename from range??clusive, to match
AbstractList.
* java/util/LinkedList.java (checkBoundsExclusive),
(checkBoundsInclusive): ditto
* java/util/Vector.java (checkBoundExclusive),
(checkBoundInclusive): Move bounds checking into common methods.

2001-12-15  Eric Blake  <ebb9@email.byu.edu>

* java/util/AbstractList.java:
(modCount): Make sure it is updated in all needed places.
* java/util/ArrayList.java: Improve javadoc. Implements
RandomAccess. Add serialVersionUID. Reorder methods.
(modCount): Make sure it is updated in all needed places.
(rangeExclusive, rangeInclusive): Add common methods for bounds
check.
(isEmpty): Add missing method.
* java/util/Collections.java: (class SynchronizedList): Make
package visible.
* java/util/ConcurrentModificationException.java: Improve
javadoc.
* java/util/EmptyStackException.java: Improve javadoc.
* java/util/LinkedList.java: Improve javadoc.
(modCount): Make sure it is updated in all needed places.
(rangeExclusive, rangeInclusive): Add common methods for bounds
check.
* java/util/NoSuchElementException.java: Improve javadoc.
* java/util/Stack.java: Improve javadoc. Fix synchronization
issues.
(modCount): Make sure it is updated in all needed places.
* java/util/Vector.java: Improve javadoc. Fix synchronization
issues. Implements RandomAccess. Reorder methods.
(modCount): Make sure it is updated in all needed places.
(setSize): Fix according to specifications: this does not dictate
the backing array size.
(removeAll, retainAll): Faster implementations.

2001-12-15  Eric Blake  <ebb9@email.byu.edu>

* java/util/BitSet.java: Improve javadoc.
(cardinality(), clear(), clear(int, int), flip(int)),
(flip(int, int), get(int, int), intersects(BitSet), isEmpty()),
(nextClearBit(int), nextSetBit(int), set(int, boolean)),
(set(int, int), set(int, int, boolean)): Add new JDK 1.4 methods.
(clone): Fix so subclasses clone correctly.

2001-12-15  Eric Blake  <ebb9@email.byu.edu>

* java/util/AbstractCollection.java: Improve javadoc.
(AbstractCollection()): Make constructor protected.
(equals(Object, Object), hashCode(Object)): Add utility methods.
* java/util/AbstractList.java: Improve javadoc.
(AbstractList()): Make constructor protected.
(indexOf(Object)): Call listIterator(), not listIterator(int).
(iterator()): Follow Sun's requirement to not use listIterator(0).
(listIterator(int)): Make AbstractListItr anonymous.
(subList(int, int)): Add support for RandomAccess.
(SubList.add(int, Object), SubList.remove(Object)): Fix bug with
modCount tracking.
(SubList.addAll(Collection)): Add missing method.
(SubList.listIterator(int)): Fix bugs in indexing, modCount
tracking.
(class RandomAccessSubList): Add new class.
* java/util/AbstractMap.java: Improve javadoc.
(keys, values, KEYS, VALUES, ENTRIES): Consolidate common map
fields.
(AbstractMap()): Make constructor protected.
(equals(Object, Object), hashCode(Object)): Add utility methods.
(equals(Object)): Change algorithm to
entrySet().equals(m.entrySet()), as documented by Sun.
(keySet(), values()): Cache the collections.
* java/util/AbstractSequentialList.java: Improve javadoc.
(AbstractSequentialList()): Make constructor protected.
* java/util/AbstractSet.java: Improve javadoc.
(AbstractSet()): Make constructor protected.
(removeAll(Collection)): Add missing method.
* java/util/Arrays.java: Improve javadoc, rearrange method orders.
(defaultComparator): Remove, in favor of
Collections.compare(Object, Object, Comparator).
(binarySearch, equals, sort): Fix natural order comparison of
floats and doubles. Also improve Object comparison - when
comparator is null, use natural order.
(fill, sort): Add missing checks for IllegalArgumentException.
(sort, qsort): Fix sorting bugs, rework the code for more
legibility.
(mergeSort): Inline into sort(Object[], int, int, Comparator).
(class ArrayList): Rename from ListImpl, and make compatible with
JDK serialization. Add methods which more efficiently override
those of AbstractList.
* java/util/Collections: Improve javadoc.
(isSequential(List)): Add and use a method for deciding between
RandomAccess and sequential algorithms on lists.
(class Empty*, class Synchronized*, class Unmodifiable*): Make
compliant with JDK serializability.
(class Singleton*, class CopiesList, class RevereseComparator),
(class UnmodifiableMap.UnmodifiableEntrySet),
(class *RandomAccessList): New classes for serial compatibility.
(class Empty*, class Singleton*, class CopiesList): Add methods
which more efficiently override those of Abstract*.
(search): Inline into binarySearch(List, Object, Comparator).
(binarySearch): Make sequential search only do log(n) comparisons,
instead of n.
(copy(List, List)): Do bounds checking before starting.
(indexOfSubList, lastIndexOfSubList, list, replaceAll, rotate),
(swap): Add new JDK 1.4 methods.
(binarySearch, max, min, sort): Allow null comparator to represent
natural ordering.
(reverse(List)): Avoid unnecessary swap.
(shuffle(List, Random)): Do shuffle in-place for RandomAccess
lists.
(SingletonList.get): Fix logic bug.
(SingletonMap.entrySet): Make the entry immutable, and cache the
returned set.
(SynchronizedCollection, SynchronizedMap, UnmodifiableCollection),
(UnmodifiableMap): Detect null pointer in construction.
(SynchronizedMap, UnmodifiableMap): Cache collection views.
* java/util/BasicMapEntry: Improve javadoc.

From-SVN: r48035

27 files changed:
libjava/ChangeLog
libjava/Makefile.am
libjava/Makefile.in
libjava/java/util/AbstractCollection.java
libjava/java/util/AbstractList.java
libjava/java/util/AbstractMap.java
libjava/java/util/AbstractSequentialList.java
libjava/java/util/AbstractSet.java
libjava/java/util/ArrayList.java
libjava/java/util/Arrays.java
libjava/java/util/BasicMapEntry.java
libjava/java/util/BitSet.java
libjava/java/util/Collections.java
libjava/java/util/Dictionary.java
libjava/java/util/HashMap.java
libjava/java/util/HashSet.java
libjava/java/util/Hashtable.java
libjava/java/util/IdentityHashMap.java
libjava/java/util/LinkedHashMap.java
libjava/java/util/LinkedHashSet.java [new file with mode: 0644]
libjava/java/util/LinkedList.java
libjava/java/util/List.java
libjava/java/util/Stack.java
libjava/java/util/TreeMap.java
libjava/java/util/TreeSet.java
libjava/java/util/Vector.java
libjava/java/util/WeakHashMap.java

index 40781b01768272063744bcb077a628388b4511d2..3a3294cc366ab9bc367eee7c12d1a4e11d4f91e7 100644 (file)
@@ -1,4 +1,236 @@
+2001-12-15  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>
+
+       * java/util/BitSet.java (and): Fix off-by-one bug, don't skip part of
+       the bitset.
+       (andNot): Likewise.
+       (xor): Likewise.
+
+2001-12-15  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>
+
+       * java/util/LinkedList.java (LinkedListItr.add): Don't skip the next
+       entry.
+
+2001-12-15  Eric Blake  <ebb9@email.byu.edu>
+
+       * java/util/TreeMap.java (removeNode): Fix bug in node removal.
+
+2001-12-15  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>
+
+       * java/util/AbstractCollection.java (containsAll): Use size of the
+       correct collection for loop bound.
+       * java/util/AbstractList.java (iterator.next): Increment pos after
+       calling get on backing list.
+       (listIterator.next): Likewise.
+       * java/util/LinkedList.java (addLastEntry): Don't increment size before
+       checking for size == 0.
+       (addFirstEntry): Rearrange to match addLastEntry.
+       (add): Do not increment size before inserting the new entry.
+
+       * java/util/AbstractCollection.java (addAll): Use size of the
+       correct collection for loop bound.
+
+2001-12-15  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>
+
+       * java/util/AbstractSet.java (removeAll): Fix scoping thinko.
+       * java/util/HashMap.java (putAllInternal): Set size here.
+       * java/util/Hashtable.java (putAllInternal): New method. Copy contents
+       of a map efficiently without calling put() or putAll().
+       (Hashtable (map)): Use putAllInternal.
+       (clone): Likewise.
+
+2001-12-15  Eric Blake  <ebb9@email.byu.edu>
+
+       * java/util/Collections.java:
+       * java/util/Vector.java:
+       * java/util/WeakHashMap.java: Fix spelling errors.
+
+2001-12-15  Eric Blake  <ebb9@email.byu.edu>
+
+       * java/util/AbstractCollection.java (removeAllInternal),
+       (retainAllInternal): Add hooks for use by ArrayList.
+       * java/util/AbstractList.java: Minor code updates. Fix some
+       scoping.
+       * java/util/AbstractMap.java: ditto
+       * java/util/ArrayList.java (readObject, writeObject): ditto
+       (removeAllInternal, retainAllInternal): Optimize.
+       * java/util/Arrays.java: ditto
+       * java/util/Collections.java: ditto. Change order of parameters
+       to equals(Object, Object) to match specs.
+       * java/util/Dictionary.java: Improve javadoc.
+       (Dictionary): Add explicit constructor.
+       * java/util/HashMap.java: Improve javadoc. Rearrange methods to
+       follow order in JDK. Cleanups related to recent code migration to
+       AbstractMap. Fix some scoping.
+       (entrySet): Cache the result.
+       (modCount): Ensure that this is updated correctly.
+       * java/util/HashSet.java: Improve javadoc. Fix some scoping.
+       (init): Add hooks for LinkedHashSet.
+       (map): Use "" instead of Boolean.TRUE in backing map. Use
+       package-private API where possible for less overhead.
+       (readObject, writeObject): Fix serialization.
+       * java/util/Hashtable.java: Improve javadoc. Fix some scoping.
+       (entrySet, keySet, values): Cache the result.
+       (modCount): Ensure that this is updated correctly.
+       (contains, remove): Fix NullPointer checking to match specs.
+       (class Enumeration): Make more like HashIterator.
+       * java/util/IdentityHashMap.java: Minor code updates.
+       (modCount): Ensure that this is updated correctly.
+       (readObject, writeObject): Fix serialization.
+       * java/util/LinkedHashMap.java: Minor code updates. Cleanups
+       related to recent code migration to AbstractMap.
+       * java/util/LinkedHashSet.java: New file.
+       * java/util/LinkedList.java:
+       (readObject, writeObject): Fix serialization.
+       * java/util/Makefile.am: List recently added files.
+       * java/util/Stack.java: Minor code updates.
+       * java/util/TreeMap.java: Improve javadoc. Overhaul the class to
+       be more efficient. Fix some scoping. Rearrange the methods.
+       (nil): Ensure that this can be thread-safe, and make it a static
+       final. Initialize it to be more useful as a sentinal node.
+       (Node): Specify color in constructor.
+       (deleteFixup, insertFixup): Improve comments and algorithm.
+       (fabricateTree): Redesign with less overhead.
+       (lowestGreaterThan): Add parameter first to make SubMap easier.
+       (removeNode): Patch hole where nil was being modified. Choose
+       predecessor instead of successor so in-place swap works.
+       (class VerifyResult, verifyTree, verifySub, verifyError): Remove
+       this dead code after verifying the class works.
+       (class SubMap): Rewrite several algorithms to avoid problems with
+       comparing nil.
+       * java/util/TreeSet.java: Improve javadoc. Fix some scoping.
+       (clone): Fix ClassCastException when cloning subSet().
+       (readObject, writeObject): Fix serialization.
+       * java/util/WeakHashMap.java: Improve javadoc. Fix some scoping.
+       (NULL_KEY): Make it compare as null, for ease elsewhere.
+       (Class WeakEntry): Rename from Entry, to avoid shadowing
+       Map.Entry. Add missing toString.
+       (modCount): Ensure that this is updated correctly.
+       (clear, containsValue, keySet, putAll, values, WeakHashMap(Map)):
+       Add missing methods and constructor.
+
+2001-12-15  Eric Blake  <ebb9@email.byu.edu>
+
+       * java/util/ArrayList.java (checkBoundExclusive),
+       (checkBoundInclusive): Rename from range??clusive, to match
+       AbstractList.
+       * java/util/LinkedList.java (checkBoundsExclusive),
+       (checkBoundsInclusive): ditto
+       * java/util/Vector.java (checkBoundExclusive),
+       (checkBoundInclusive): Move bounds checking into common methods.
+
+2001-12-15  Eric Blake  <ebb9@email.byu.edu>
+
+       * java/util/AbstractList.java:
+       (modCount): Make sure it is updated in all needed places.
+       * java/util/ArrayList.java: Improve javadoc. Implements
+       RandomAccess. Add serialVersionUID. Reorder methods.
+       (modCount): Make sure it is updated in all needed places.
+       (rangeExclusive, rangeInclusive): Add common methods for bounds
+       check.
+       (isEmpty): Add missing method.
+       * java/util/Collections.java: (class SynchronizedList): Make
+       package visible.
+       * java/util/ConcurrentModificationException.java: Improve
+       javadoc.
+       * java/util/EmptyStackException.java: Improve javadoc.
+       * java/util/LinkedList.java: Improve javadoc.
+       (modCount): Make sure it is updated in all needed places.
+       (rangeExclusive, rangeInclusive): Add common methods for bounds
+       check.
+       * java/util/NoSuchElementException.java: Improve javadoc.
+       * java/util/Stack.java: Improve javadoc. Fix synchronization
+       issues.
+       (modCount): Make sure it is updated in all needed places.
+       * java/util/Vector.java: Improve javadoc. Fix synchronization
+       issues. Implements RandomAccess. Reorder methods.
+       (modCount): Make sure it is updated in all needed places.
+       (setSize): Fix according to specifications: this does not dictate
+       the backing array size.
+       (removeAll, retainAll): Faster implementations.
+
+2001-12-15  Eric Blake  <ebb9@email.byu.edu>
+
+       * java/util/BitSet.java: Improve javadoc.
+       (cardinality(), clear(), clear(int, int), flip(int)),
+       (flip(int, int), get(int, int), intersects(BitSet), isEmpty()),
+       (nextClearBit(int), nextSetBit(int), set(int, boolean)),
+       (set(int, int), set(int, int, boolean)): Add new JDK 1.4 methods.
+       (clone): Fix so subclasses clone correctly.
+
+2001-12-15  Eric Blake  <ebb9@email.byu.edu>
+
+       * java/util/AbstractCollection.java: Improve javadoc.
+       (AbstractCollection()): Make constructor protected.
+       (equals(Object, Object), hashCode(Object)): Add utility methods.
+       * java/util/AbstractList.java: Improve javadoc.
+       (AbstractList()): Make constructor protected.
+       (indexOf(Object)): Call listIterator(), not listIterator(int).
+       (iterator()): Follow Sun's requirement to not use listIterator(0).
+       (listIterator(int)): Make AbstractListItr anonymous.
+       (subList(int, int)): Add support for RandomAccess.
+       (SubList.add(int, Object), SubList.remove(Object)): Fix bug with
+       modCount tracking.
+       (SubList.addAll(Collection)): Add missing method.
+       (SubList.listIterator(int)): Fix bugs in indexing, modCount
+       tracking.
+       (class RandomAccessSubList): Add new class.
+       * java/util/AbstractMap.java: Improve javadoc.
+       (keys, values, KEYS, VALUES, ENTRIES): Consolidate common map
+       fields.
+       (AbstractMap()): Make constructor protected.
+       (equals(Object, Object), hashCode(Object)): Add utility methods.
+       (equals(Object)): Change algorithm to
+       entrySet().equals(m.entrySet()), as documented by Sun.
+       (keySet(), values()): Cache the collections.
+       * java/util/AbstractSequentialList.java: Improve javadoc.
+       (AbstractSequentialList()): Make constructor protected.
+       * java/util/AbstractSet.java: Improve javadoc.
+       (AbstractSet()): Make constructor protected.
+       (removeAll(Collection)): Add missing method.
+       * java/util/Arrays.java: Improve javadoc, rearrange method orders.
+       (defaultComparator): Remove, in favor of
+       Collections.compare(Object, Object, Comparator).
+       (binarySearch, equals, sort): Fix natural order comparison of
+       floats and doubles. Also improve Object comparison - when
+       comparator is null, use natural order.
+       (fill, sort): Add missing checks for IllegalArgumentException.
+       (sort, qsort): Fix sorting bugs, rework the code for more
+       legibility.
+       (mergeSort): Inline into sort(Object[], int, int, Comparator).
+       (class ArrayList): Rename from ListImpl, and make compatible with
+       JDK serialization. Add methods which more efficiently override
+       those of AbstractList.
+       * java/util/Collections: Improve javadoc.
+       (isSequential(List)): Add and use a method for deciding between
+       RandomAccess and sequential algorithms on lists.
+       (class Empty*, class Synchronized*, class Unmodifiable*): Make
+       compliant with JDK serializability.
+       (class Singleton*, class CopiesList, class RevereseComparator),
+       (class UnmodifiableMap.UnmodifiableEntrySet),
+       (class *RandomAccessList): New classes for serial compatibility.
+       (class Empty*, class Singleton*, class CopiesList): Add methods
+       which more efficiently override those of Abstract*.
+       (search): Inline into binarySearch(List, Object, Comparator).
+       (binarySearch): Make sequential search only do log(n) comparisons,
+       instead of n.
+       (copy(List, List)): Do bounds checking before starting.
+       (indexOfSubList, lastIndexOfSubList, list, replaceAll, rotate),
+       (swap): Add new JDK 1.4 methods.
+       (binarySearch, max, min, sort): Allow null comparator to represent
+       natural ordering.
+       (reverse(List)): Avoid unnecessary swap.
+       (shuffle(List, Random)): Do shuffle in-place for RandomAccess
+       lists.
+       (SingletonList.get): Fix logic bug.
+       (SingletonMap.entrySet): Make the entry immutable, and cache the
+       returned set.
+       (SynchronizedCollection, SynchronizedMap, UnmodifiableCollection),
+       (UnmodifiableMap): Detect null pointer in construction.
+       (SynchronizedMap, UnmodifiableMap): Cache collection views.
+       * java/util/BasicMapEntry: Improve javadoc.
+
 2001-12-14  Hans Boehm  <Hans_Boehm@hp.com>
+
        * libjava/prims.cc: Some old cleanups.  The collector now
        handles test for out of memory.
 
index 551be461cb1c749c0c2117732991128c74da49a4..277995b37049e16542317ac79150be731d1d414d 100644 (file)
@@ -1200,6 +1200,7 @@ java/util/IdentityHashMap.java \
 java/util/Iterator.java        \
 java/util/LinkedList.java \
 java/util/LinkedHashMap.java \
+java/util/LinkedHashSet.java \
 java/util/List.java \
 java/util/ListIterator.java \
 java/util/ListResourceBundle.java \
index 60d003908f23e3c7ec7bb555056f3f90273e21ea..57471773dd38a69542d3bed3a113dfccc384a4a4 100644 (file)
@@ -123,19 +123,13 @@ libgcj_basedir = @libgcj_basedir@
 mkinstalldirs = @mkinstalldirs@
 
 AUTOMAKE_OPTIONS = foreign
-@TESTSUBDIR_TRUE@SUBDIRS = \
-@TESTSUBDIR_TRUE@$(DIRLTDL) testsuite gcj include
-@TESTSUBDIR_FALSE@SUBDIRS = \
-@TESTSUBDIR_FALSE@$(DIRLTDL) gcj include
-@USE_LIBDIR_TRUE@toolexeclibdir = \
-@USE_LIBDIR_TRUE@$(libdir)$(MULTISUBDIR)
-@USE_LIBDIR_FALSE@toolexeclibdir = \
-@USE_LIBDIR_FALSE@$(toolexecdir)/lib$(MULTISUBDIR)
-@USE_LIBDIR_FALSE@toolexecdir = \
-@USE_LIBDIR_FALSE@$(exec_prefix)/$(target_alias)
-@XLIB_AWT_TRUE@cond_x_ltlibrary = \
-@XLIB_AWT_TRUE@libgcjx.la
-@XLIB_AWT_FALSE@cond_x_ltlibrary = \
+@TESTSUBDIR_TRUE@SUBDIRS = @TESTSUBDIR_TRUE@$(DIRLTDL) testsuite gcj include
+@TESTSUBDIR_FALSE@SUBDIRS = @TESTSUBDIR_FALSE@$(DIRLTDL) gcj include
+@USE_LIBDIR_TRUE@toolexeclibdir = @USE_LIBDIR_TRUE@$(libdir)$(MULTISUBDIR)
+@USE_LIBDIR_FALSE@toolexeclibdir = @USE_LIBDIR_FALSE@$(toolexecdir)/lib$(MULTISUBDIR)
+@USE_LIBDIR_FALSE@toolexecdir = @USE_LIBDIR_FALSE@$(exec_prefix)/$(target_alias)
+@XLIB_AWT_TRUE@cond_x_ltlibrary = @XLIB_AWT_TRUE@libgcjx.la
+@XLIB_AWT_FALSE@cond_x_ltlibrary = 
 
 toolexeclib_LTLIBRARIES = libgcj.la $(cond_x_ltlibrary)
 toolexeclib_DATA = libgcj.spec
@@ -143,20 +137,14 @@ data_DATA = libgcj.jar
 
 secdir = $(libdir)/security
 
-@NATIVE_TRUE@bin_PROGRAMS = \
-@NATIVE_TRUE@jv-convert gij rmic rmiregistry
+@NATIVE_TRUE@bin_PROGRAMS = @NATIVE_TRUE@jv-convert gij rmic rmiregistry
 
 bin_SCRIPTS = addr2name.awk
-@CANADIAN_TRUE@@NULL_TARGET_TRUE@ZIP = \
-@CANADIAN_TRUE@@NULL_TARGET_TRUE@$(MULTIBUILDTOP)../$(COMPPATH)/fastjar/jar$(EXEEXT)
-@CANADIAN_TRUE@@NULL_TARGET_FALSE@ZIP = \
-@CANADIAN_TRUE@@NULL_TARGET_FALSE@jar
-@CANADIAN_FALSE@ZIP = \
-@CANADIAN_FALSE@$(MULTIBUILDTOP)../$(COMPPATH)/fastjar/jar$(EXEEXT)
-@CANADIAN_TRUE@GCJH = \
-@CANADIAN_TRUE@gcjh
-@CANADIAN_FALSE@GCJH = \
-@CANADIAN_FALSE@$(MULTIBUILDTOP)../$(COMPPATH)/gcc/gcjh$(EXEEXT)
+@CANADIAN_TRUE@@NULL_TARGET_TRUE@ZIP = @CANADIAN_TRUE@@NULL_TARGET_TRUE@$(MULTIBUILDTOP)../$(COMPPATH)/fastjar/jar$(EXEEXT)
+@CANADIAN_TRUE@@NULL_TARGET_FALSE@ZIP = @CANADIAN_TRUE@@NULL_TARGET_FALSE@jar
+@CANADIAN_FALSE@ZIP = @CANADIAN_FALSE@$(MULTIBUILDTOP)../$(COMPPATH)/fastjar/jar$(EXEEXT)
+@CANADIAN_TRUE@GCJH = @CANADIAN_TRUE@gcjh
+@CANADIAN_FALSE@GCJH = @CANADIAN_FALSE@$(MULTIBUILDTOP)../$(COMPPATH)/gcc/gcjh$(EXEEXT)
 
 GCJ_WITH_FLAGS = $(GCJ) --encoding=UTF-8
 
@@ -176,10 +164,8 @@ AM_CXXFLAGS = -fno-rtti -fnon-call-exceptions \
        @LIBGCJ_CXXFLAGS@ @X_CFLAGS@ $(WARNINGS) -D_GNU_SOURCE \
        -DPREFIX="\"$(prefix)\""
 
-@USING_GCC_TRUE@AM_CFLAGS = \
-@USING_GCC_TRUE@@LIBGCJ_CFLAGS@ $(WARNINGS)
-@USING_GCC_FALSE@AM_CFLAGS = \
-@USING_GCC_FALSE@@LIBGCJ_CFLAGS@
+@USING_GCC_TRUE@AM_CFLAGS = @USING_GCC_TRUE@@LIBGCJ_CFLAGS@ $(WARNINGS)
+@USING_GCC_FALSE@AM_CFLAGS = @USING_GCC_FALSE@@LIBGCJ_CFLAGS@
 
 JCFLAGS = -g
 JC1FLAGS = @LIBGCJ_JAVAFLAGS@ $(GCJFLAGS)
@@ -252,8 +238,7 @@ extra_headers = java/lang/Object.h java/lang/Class.h
 
 NM = nm
 
-@NATIVE_TRUE@@MAINTAINER_MODE_TRUE@noinst_PROGRAMS = \
-@NATIVE_TRUE@@MAINTAINER_MODE_TRUE@gen-from-JIS
+@NATIVE_TRUE@@MAINTAINER_MODE_TRUE@noinst_PROGRAMS = @NATIVE_TRUE@@MAINTAINER_MODE_TRUE@gen-from-JIS
 
 CONVERT_DIR = gnu/gcj/convert
 
@@ -950,6 +935,7 @@ java/util/IdentityHashMap.java \
 java/util/Iterator.java        \
 java/util/LinkedList.java \
 java/util/LinkedHashMap.java \
+java/util/LinkedHashSet.java \
 java/util/List.java \
 java/util/ListIterator.java \
 java/util/ListResourceBundle.java \
@@ -1585,7 +1571,7 @@ libgcj-test.spec.in libgcj.spec.in
 
 DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
 
-TAR = tar
+TAR = gtar
 GZIP_ENV = --best
 DIST_SUBDIRS =  @DIRLTDL@ testsuite gcj include @DIRLTDL@ gcj include
 DEP_FILES =  .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \
@@ -2224,10 +2210,11 @@ DEP_FILES =  .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \
 .deps/java/util/GregorianCalendar.P .deps/java/util/HashMap.P \
 .deps/java/util/HashSet.P .deps/java/util/Hashtable.P \
 .deps/java/util/IdentityHashMap.P .deps/java/util/Iterator.P \
-.deps/java/util/LinkedHashMap.P .deps/java/util/LinkedList.P \
-.deps/java/util/List.P .deps/java/util/ListIterator.P \
-.deps/java/util/ListResourceBundle.P .deps/java/util/Locale.P \
-.deps/java/util/Map.P .deps/java/util/MissingResourceException.P \
+.deps/java/util/LinkedHashMap.P .deps/java/util/LinkedHashSet.P \
+.deps/java/util/LinkedList.P .deps/java/util/List.P \
+.deps/java/util/ListIterator.P .deps/java/util/ListResourceBundle.P \
+.deps/java/util/Locale.P .deps/java/util/Map.P \
+.deps/java/util/MissingResourceException.P \
 .deps/java/util/NoSuchElementException.P .deps/java/util/Observable.P \
 .deps/java/util/Observer.P .deps/java/util/Properties.P \
 .deps/java/util/PropertyPermission.P \
@@ -2735,7 +2722,7 @@ distdir: $(DISTFILES)
        @for file in $(DISTFILES); do \
          d=$(srcdir); \
          if test -d $$d/$$file; then \
-           cp -pr $$/$$file $(distdir)/$$file; \
+           cp -pr $$d/$$file $(distdir)/$$file; \
          else \
            test -f $(distdir)/$$file \
            || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
index 1bb73d7d8b4cbbb8f8b7513c5e469bd0cea4f410..095e1c418808a9e0d22cc411e4a8bcf97c57f24c 100644 (file)
@@ -1,5 +1,5 @@
 /* AbstractCollection.java -- Abstract implementation of most of Collection
-   Copyright (C) 1998, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -42,77 +42,119 @@ import java.lang.reflect.Array;
  * backing data structure allows for a more efficient implementation. The
  * precise implementation used by AbstractCollection is documented, so that
  * subclasses can tell which methods could be implemented more efficiently.
+ * <p>
+ *
+ * The programmer should provide a no-argument constructor, and one that
+ * accepts another Collection, as recommended by the Collection interface.
+ * Unfortunately, there is no way to enforce this in Java.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see AbstractSet
+ * @see AbstractList
+ * @since 1.2
+ * @status updated to 1.4
  */
 public abstract class AbstractCollection implements Collection
 {
+  /**
+   * The main constructor, for use by subclasses.
+   */
+  protected AbstractCollection()
+  {
+  }
+
   /**
    * Return an Iterator over this collection. The iterator must provide the
    * hasNext and next methods and should in addition provide remove if the
    * collection is modifiable.
+   *
+   * @return an iterator
    */
   public abstract Iterator iterator();
 
   /**
-   * Return the number of elements in this collection.
+   * Return the number of elements in this collection. If there are more than
+   * Integer.MAX_VALUE elements, return Integer.MAX_VALUE.
+   *
+   * @return the size
    */
   public abstract int size();
 
   /**
-   * Add an object to the collection. This implementation always throws an
-   * UnsupportedOperationException - it should be overridden if the collection
-   * is to be modifiable.
+   * Add an object to the collection (optional operation). This implementation
+   * always throws an UnsupportedOperationException - it should be
+   * overridden if the collection is to be modifiable. If the collection
+   * does not accept duplicates, simply return false. Collections may specify
+   * limitations on what may be added.
    *
    * @param o the object to add
    * @return true if the add operation caused the Collection to change
-   * @exception UnsupportedOperationException if the add operation is not
-   *   supported on this collection
+   * @throws UnsupportedOperationException if the add operation is not
+   *         supported on this collection
+   * @throws NullPointerException if the collection does not support null
+   * @throws ClassCastException if the object is of the wrong type
+   * @throws IllegalArgumentException if some aspect of the object prevents
+   *         it from being added
    */
   public boolean add(Object o)
   {
-    throw new java.lang.UnsupportedOperationException();
+    throw new UnsupportedOperationException();
   }
 
   /**
-   * Add all the elements of a given collection to this collection. This
-   * implementation obtains an Iterator over the given collection and iterates
-   * over it, adding each element with the add(Object) method (thus this method
-   * will fail with an UnsupportedOperationException if the add method does).
+   * Add all the elements of a given collection to this collection (optional
+   * operation). This implementation obtains an Iterator over the given
+   * collection and iterates over it, adding each element with the
+   * add(Object) method (thus this method will fail with an
+   * UnsupportedOperationException if the add method does). The behavior is
+   * unspecified if the specified collection is modified during the iteration,
+   * including the special case of trying addAll(this) on a non-empty
+   * collection.
    *
    * @param c the collection to add the elements of to this collection
    * @return true if the add operation caused the Collection to change
-   * @exception UnsupportedOperationException if the add operation is not
-   *   supported on this collection
+   * @throws UnsupportedOperationException if the add operation is not
+   *         supported on this collection
+   * @throws NullPointerException if this collection does not support null,
+   *         or if the specified collection is null
+   * @throws ClassCastException if an object in c is of the wrong type
+   * @throws IllegalArgumentException if some aspect of an object in c prevents
+   *         it from being added
+   * @see #add(Object)
    */
   public boolean addAll(Collection c)
   {
     Iterator itr = c.iterator();
-    int size = c.size();
     boolean modified = false;
-    for (int pos = 0; pos < size; pos++)
-      {
-       modified |= add(itr.next());
-      }
+    int pos = c.size();
+    while (--pos >= 0)
+      modified |= add(itr.next());
     return modified;
   }
 
   /**
-   * Remove all elements from the collection. This implementation obtains an
-   * iterator over the collection and calls next and remove on it repeatedly
-   * (thus this method will fail with an UnsupportedOperationException if the
-   * Iterator's remove method does) until there are no more elements to remove.
+   * Remove all elements from the collection (optional operation). This
+   * implementation obtains an iterator over the collection and calls next
+   * and remove on it repeatedly (thus this method will fail with an
+   * UnsupportedOperationException if the Iterator's remove method does)
+   * until there are no more elements to remove.
    * Many implementations will have a faster way of doing this.
    *
-   * @exception UnsupportedOperationException if the Iterator returned by
-   *   iterator does not provide an implementation of remove
+   * @throws UnsupportedOperationException if the Iterator returned by
+   *         iterator does not provide an implementation of remove
+   * @see Iterator#remove()
    */
   public void clear()
   {
     Iterator itr = iterator();
-    int size = size();
-    for (int pos = 0; pos < size; pos++)
+    int pos = size();
+    while (--pos >= 0)
       {
-       itr.next();
-       itr.remove();
+        itr.next();
+        itr.remove();
       }
   }
 
@@ -130,12 +172,10 @@ public abstract class AbstractCollection implements Collection
   public boolean contains(Object o)
   {
     Iterator itr = iterator();
-    int size = size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       if (o == null ? itr.next() == null : o.equals(itr.next()))
-         return true;
-      }
+    int pos = size();
+    while (--pos >= 0)
+      if (equals(o, itr.next()))
+        return true;
     return false;
   }
 
@@ -147,17 +187,17 @@ public abstract class AbstractCollection implements Collection
    *
    * @param c the collection to test against
    * @return true if this collection contains all the elements in the given
-   *   collection
+   *         collection
+   * @throws NullPointerException if the given collection is null
+   * @see #contains(Object)
    */
   public boolean containsAll(Collection c)
   {
     Iterator itr = c.iterator();
-    int size = c.size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       if (!contains(itr.next()))
-         return false;
-      }
+    int pos = c.size();
+    while (--pos >= 0)
+      if (!contains(itr.next()))
+        return false;
     return true;
   }
 
@@ -166,6 +206,7 @@ public abstract class AbstractCollection implements Collection
    * size() == 0.
    *
    * @return true if this collection is empty.
+   * @see #size()
    */
   public boolean isEmpty()
   {
@@ -173,92 +214,131 @@ public abstract class AbstractCollection implements Collection
   }
 
   /**
-   * Remove a single instance of an object from this collection. That is,
-   * remove one element e such that (o == null ? e == null : o.equals(e)), if
-   * such an element exists. This implementation obtains an iterator over the
-   * collection and iterates over it, testing each element for equality with
-   * the given object. If it is equal, it is removed by the iterator's remove
-   * method (thus this method will fail with an UnsupportedOperationException
-   * if the Iterator's remove method does). After the first element has been
+   * Remove a single instance of an object from this collection (optional
+   * operation). That is, remove one element e such that
+   * <code>(o == null ? e == null : o.equals(e))</code>, if such an element
+   * exists. This implementation obtains an iterator over the collection
+   * and iterates over it, testing each element for equality with the given
+   * object. If it is equal, it is removed by the iterator's remove method
+   * (thus this method will fail with an UnsupportedOperationException if
+   * the Iterator's remove method does). After the first element has been
    * removed, true is returned; if the end of the collection is reached, false
    * is returned.
    *
    * @param o the object to remove from this collection
    * @return true if the remove operation caused the Collection to change, or
-   *   equivalently if the collection did contain o.
-   * @exception UnsupportedOperationException if this collection's Iterator
-   *   does not support the remove method
+   *         equivalently if the collection did contain o.
+   * @throws UnsupportedOperationException if this collection's Iterator
+   *         does not support the remove method
+   * @see Iterator#remove()
    */
   public boolean remove(Object o)
   {
     Iterator itr = iterator();
-    int size = size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       if (o == null ? itr.next() == null : o.equals(itr.next()))
-         {
-           itr.remove();
-           return true;
-         }
-      }
+    int pos = size();
+    while (--pos >= 0)
+      if (equals(o, itr.next()))
+        {
+          itr.remove();
+          return true;
+        }
     return false;
   }
 
   /**
    * Remove from this collection all its elements that are contained in a given
-   * collection. This implementation iterates over this collection, and for
-   * each element tests if it is contained in the given collection. If so, it
-   * is removed by the Iterator's remove method (thus this method will fail
-   * with an UnsupportedOperationException if the Iterator's remove method
-   * does).
+   * collection (optional operation). This implementation iterates over this
+   * collection, and for each element tests if it is contained in the given
+   * collection. If so, it is removed by the Iterator's remove method (thus
+   * this method will fail with an UnsupportedOperationException if the
+   * Iterator's remove method does).
    *
    * @param c the collection to remove the elements of
    * @return true if the remove operation caused the Collection to change
-   * @exception UnsupportedOperationException if this collection's Iterator
-   *   does not support the remove method
+   * @throws UnsupportedOperationException if this collection's Iterator
+   *         does not support the remove method
+   * @see Iterator#remove()
    */
   public boolean removeAll(Collection c)
+  {
+    return removeAllInternal(c);
+  }
+
+  /**
+   * Remove from this collection all its elements that are contained in a given
+   * collection (optional operation). This implementation iterates over this
+   * collection, and for each element tests if it is contained in the given
+   * collection. If so, it is removed by the Iterator's remove method (thus
+   * this method will fail with an UnsupportedOperationException if the
+   * Iterator's remove method does). This method is necessary for ArrayList,
+   * which cannot publicly override removeAll but can optimize this call.
+   *
+   * @param c the collection to remove the elements of
+   * @return true if the remove operation caused the Collection to change
+   * @throws UnsupportedOperationException if this collection's Iterator
+   *         does not support the remove method
+   * @see Iterator#remove()
+   */
+  boolean removeAllInternal(Collection c)
   {
     Iterator itr = iterator();
-    int size = size();
     boolean modified = false;
-    for (int pos = 0; pos < size; pos++)
-      {
-       if (c.contains(itr.next()))
-         {
-           itr.remove();
-           modified = true;
-         }
-      }
+    int pos = size();
+    while (--pos >= 0)
+      if (c.contains(itr.next()))
+        {
+          itr.remove();
+          modified = true;
+        }
     return modified;
   }
 
   /**
    * Remove from this collection all its elements that are not contained in a
-   * given collection. This implementation iterates over this collection, and
-   * for each element tests if it is contained in the given collection. If not,
-   * it is removed by the Iterator's remove method (thus this method will fail
-   * with an UnsupportedOperationException if the Iterator's remove method
-   * does).
+   * given collection (optional operation). This implementation iterates over
+   * this collection, and for each element tests if it is contained in the
+   * given collection. If not, it is removed by the Iterator's remove method
+   * (thus this method will fail with an UnsupportedOperationException if
+   * the Iterator's remove method does).
    *
    * @param c the collection to retain the elements of
    * @return true if the remove operation caused the Collection to change
-   * @exception UnsupportedOperationException if this collection's Iterator
-   *   does not support the remove method
+   * @throws UnsupportedOperationException if this collection's Iterator
+   *         does not support the remove method
+   * @see Iterator#remove()
    */
   public boolean retainAll(Collection c)
+  {
+    return retainAllInternal(c);
+  }
+
+  /**
+   * Remove from this collection all its elements that are not contained in a
+   * given collection (optional operation). This implementation iterates over
+   * this collection, and for each element tests if it is contained in the
+   * given collection. If not, it is removed by the Iterator's remove method
+   * (thus this method will fail with an UnsupportedOperationException if
+   * the Iterator's remove method does). This method is necessary for
+   * ArrayList, which cannot publicly override retainAll but can optimize
+   * this call.
+   *
+   * @param c the collection to retain the elements of
+   * @return true if the remove operation caused the Collection to change
+   * @throws UnsupportedOperationException if this collection's Iterator
+   *         does not support the remove method
+   * @see Iterator#remove()
+   */
+  boolean retainAllInternal(Collection c)
   {
     Iterator itr = iterator();
-    int size = size();
     boolean modified = false;
-    for (int pos = 0; pos < size; pos++)
-      {
-       if (!c.contains(itr.next()))
-         {
-           itr.remove();
-           modified = true;
-         }
-      }
+    int pos = size();
+    while (--pos >= 0)
+      if (!c.contains(itr.next()))
+        {
+          itr.remove();
+          modified = true;
+        }
     return modified;
   }
 
@@ -266,18 +346,18 @@ public abstract class AbstractCollection implements Collection
    * Return an array containing the elements of this collection. This
    * implementation creates an Object array of size size() and then iterates
    * over the collection, setting each element of the array from the value
-   * returned by the iterator.
+   * returned by the iterator. The returned array is safe, and is not backed
+   * by the collection.
    *
    * @return an array containing the elements of this collection
    */
   public Object[] toArray()
   {
     Iterator itr = iterator();
-    Object[]a = new Object[size()];
-    for (int pos = 0; pos < a.length; pos++)
-      {
-       a[pos] = itr.next();
-      }
+    int size = size();
+    Object[] a = new Object[size];
+    for (int pos = 0; pos < size; pos++)
+      a[pos] = itr.next();
     return a;
   }
 
@@ -293,29 +373,29 @@ public abstract class AbstractCollection implements Collection
    * obtained over the collection and the elements are placed in the array as
    * they are returned by the iterator. Finally the first spare element, if
    * any, of the array is set to null, and the created array is returned.
+   * The returned array is safe; it is not backed by the collection. Note that
+   * null may not mark the last element, if the collection allows null
+   * elements.
    *
    * @param a the array to copy into, or of the correct run-time type
    * @return the array that was produced
-   * @exception ClassCastException if the type of the array precludes holding
-   *   one of the elements of the Collection
+   * @throws NullPointerException if the given array is null
+   * @throws ArrayStoreException if the type of the array precludes holding
+   *         one of the elements of the Collection
    */
-  public Object[] toArray(Object[]a)
+  public Object[] toArray(Object[] a)
   {
     int size = size();
     if (a.length < size)
-      {
-       a = (Object[])Array.newInstance(a.getClass().getComponentType(),
-                                       size);
-      }
+      a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
+                                       size);
+    else if (a.length > size)
+      a[size] = null;
+
     Iterator itr = iterator();
     for (int pos = 0; pos < size; pos++)
-      {
-       a[pos] = itr.next();
-      }
-    if (a.length > size)
-      {
-       a[size] = null;
-      }
+      a[pos] = itr.next();
+
     return a;
   }
 
@@ -331,15 +411,41 @@ public abstract class AbstractCollection implements Collection
   public String toString()
   {
     Iterator itr = iterator();
-    int size = size();
     StringBuffer r = new StringBuffer("[");
-    for (int pos = 0; pos < size; pos++)
+    for (int pos = size(); pos > 0; pos--)
       {
-       r.append(itr.next());
-       if (pos < size - 1)
-         r.append(", ");
+        r.append(itr.next());
+        if (pos > 1)
+          r.append(", ");
       }
     r.append("]");
     return r.toString();
   }
+
+  /**
+   * Compare two objects according to Collection semantics.
+   *
+   * @param o1 the first object
+   * @param o2 the second object
+   * @return o1 == null ? o2 == null : o1.equals(o2)
+   */
+  // Package visible for use throughout java.util.
+  // It may be inlined since it is final.
+  static final boolean equals(Object o1, Object o2)
+  {
+    return o1 == null ? o2 == null : o1.equals(o2);
+  }
+
+  /**
+   * Hash an object according to Collection semantics.
+   *
+   * @param o the object to hash
+   * @return o1 == null ? 0 : o1.hashCode()
+   */
+  // Package visible for use throughout java.util.
+  // It may be inlined since it is final.
+  static final int hashCode(Object o)
+  {
+    return o == null ? 0 : o.hashCode();
+  }
 }
index ba589e335e74ba572efcda4bf78b53c3d8f8fc89..714f92a7bc4a296fd09f394c8ab7d51012b3a384 100644 (file)
@@ -1,5 +1,5 @@
 /* AbstractList.java -- Abstract implementation of most of List
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -25,67 +25,192 @@ This exception does not however invalidate any other reasons why the
 executable file might be covered by the GNU General Public License. */
 
 
-// TO DO:
-// ~ Doc comments for almost everything.
-// ~ Better general commenting
-
 package java.util;
 
 /**
  * A basic implementation of most of the methods in the List interface to make
- * it easier to create a List based on a random-access data structure. To
- * create an unmodifiable list, it is only necessary to override the size() and
- * get(int) methods (this contrasts with all other abstract collection classes
- * which require an iterator to be provided). To make the list modifiable, the
- * set(int, Object)  method should also be overridden, and to make the list
- * resizable, the add(int, Object) and remove(int) methods should be overridden
- * too. Other methods should be overridden if the backing data structure allows
- * for a more efficient implementation. The precise implementation used by
- * AbstractList is documented, so that subclasses can tell which methods could
- * be implemented more efficiently.
+ * it easier to create a List based on a random-access data structure. If
+ * the list is sequential (such as a linked list), use AbstractSequentialList.
+ * To create an unmodifiable list, it is only necessary to override the
+ * size() and get(int) methods (this contrasts with all other abstract
+ * collection classes which require an iterator to be provided). To make the
+ * list modifiable, the set(int, Object) method should also be overridden, and
+ * to make the list resizable, the add(int, Object) and remove(int) methods
+ * should be overridden too. Other methods should be overridden if the
+ * backing data structure allows for a more efficient implementation.
+ * The precise implementation used by AbstractList is documented, so that
+ * subclasses can tell which methods could be implemented more efficiently.
+ * <p>
+ *
+ * As recommended by Collection and List, the subclass should provide at
+ * least a no-argument and a Collection constructor. This class is not
+ * synchronized.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see List
+ * @see AbstractSequentialList
+ * @see AbstractCollection
+ * @see ListIterator
+ * @since 1.2
+ * @status updated to 1.4
  */
 public abstract class AbstractList extends AbstractCollection implements List
 {
   /**
    * A count of the number of structural modifications that have been made to
-   * the list (that is, insertions and removals).
+   * the list (that is, insertions and removals). Structural modifications
+   * are ones which change the list size or affect how iterations would
+   * behave. This field is available for use by Iterator and ListIterator,
+   * in order to throw a {@link ConcurrentModificationException} in response
+   * to the next operation on the iterator. This <i>fail-fast</i> behavior
+   * saves the user from many subtle bugs otherwise possible from concurrent
+   * modification during iteration.
+   * <p>
+   *
+   * To make lists fail-fast, increment this field by just 1 in the
+   * <code>add(int, Object)</code> and <code>remove(int)</code> methods.
+   * Otherwise, this field may be ignored.
+   */
+  protected int modCount;
+
+  /**
+   * The main constructor, for use by subclasses.
    */
-  protected transient int modCount = 0;
+  protected AbstractList()
+  {
+  }
 
+  /**
+   * Returns the elements at the specified position in the list.
+   *
+   * @param index the element to return
+   * @return the element at that position
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
   public abstract Object get(int index);
 
+  /**
+   * Insert an element into the list at a given position (optional operation).
+   * This shifts all existing elements from that position to the end one
+   * index to the right.  This version of add has no return, since it is
+   * assumed to always succeed if there is no exception. This implementation
+   * always throws UnsupportedOperationException, and must be overridden to
+   * make a modifiable List.  If you want fail-fast iterators, be sure to
+   * increment modCount when overriding this.
+   *
+   * @param index the location to insert the item
+   * @param o the object to insert
+   * @throws UnsupportedOperationException if this list does not support the
+   *         add operation
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   * @throws ClassCastException if o cannot be added to this list due to its
+   *         type
+   * @throws IllegalArgumentException if o cannot be added to this list for
+   *         some other reason
+   * @see #modCount
+   */
   public void add(int index, Object o)
   {
     throw new UnsupportedOperationException();
   }
 
+  /**
+   * Add an element to the end of the list (optional operation). If the list
+   * imposes restraints on what can be inserted, such as no null elements,
+   * this should be documented. This implementation calls
+   * <code>add(size(), o);</code>, and will fail if that version does.
+   *
+   * @param o the object to add
+   * @return true, as defined by Collection for a modified list
+   * @throws UnsupportedOperationException if this list does not support the
+   *         add operation
+   * @throws ClassCastException if o cannot be added to this list due to its
+   *         type
+   * @throws IllegalArgumentException if o cannot be added to this list for
+   *         some other reason
+   * @see #add(int, Object)
+   */
   public boolean add(Object o)
   {
     add(size(), o);
     return true;
   }
 
+  /**
+   * Insert the contents of a collection into the list at a given position
+   * (optional operation). Shift all elements at that position to the right
+   * by the number of elements inserted. This operation is undefined if
+   * this list is modified during the operation (for example, if you try
+   * to insert a list into itself). This implementation uses the iterator of
+   * the collection, repeatedly calling add(int, Object); this will fail
+   * if add does. This can often be made more efficient.
+   *
+   * @param index the location to insert the collection
+   * @param c the collection to insert
+   * @return true if the list was modified by this action, that is, if c is
+   *         non-empty
+   * @throws UnsupportedOperationException if this list does not support the
+   *         addAll operation
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   * @throws ClassCastException if some element of c cannot be added to this
+   *         list due to its type
+   * @throws IllegalArgumentException if some element of c cannot be added
+   *         to this list for some other reason
+   * @throws NullPointerException if the specified collection is null
+   * @see #add(int, Object)
+   */
   public boolean addAll(int index, Collection c)
   {
     Iterator itr = c.iterator();
     int size = c.size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       add(index++, itr.next());
-      }
-    return (size > 0);
+    for (int pos = size; pos > 0; pos--)
+      add(index++, itr.next());
+    return size > 0;
   }
 
+  /**
+   * Clear the list, such that a subsequent call to isEmpty() would return
+   * true (optional operation). This implementation calls
+   * <code>removeRange(0, size())</code>, so it will fail unless remove
+   * or removeRange is overridden.
+   *
+   * @throws UnsupportedOperationException if this list does not support the
+   *         clear operation
+   * @see #remove(int)
+   * @see #removeRange(int, int)
+   */
   public void clear()
   {
     removeRange(0, size());
   }
 
+  /**
+   * Test whether this list is equal to another object. A List is defined to be
+   * equal to an object if and only if that object is also a List, and the two
+   * lists have the same sequence. Two lists l1 and l2 are equal if and only
+   * if <code>l1.size() == l2.size()</code>, and for every integer n between 0
+   * and <code>l1.size() - 1</code> inclusive, <code>l1.get(n) == null ?
+   * l2.get(n) == null : l1.get(n).equals(l2.get(n))</code>.
+   * <p>
+   *
+   * This implementation returns true if the object is this, or false if the
+   * object is not a List.  Otherwise, it iterates over both lists (with
+   * iterator()), returning false if two elements compare false or one list
+   * is shorter, and true if the iteration completes successfully.
+   *
+   * @param o the object to test for equality with this list
+   * @return true if o is equal to this list
+   * @see Object#equals(Object)
+   * @see #hashCode()
+   */
   public boolean equals(Object o)
   {
     if (o == this)
       return true;
-    if (!(o instanceof List))
+    if (! (o instanceof List))
       return false;
     int size = size();
     if (size != ((List) o).size())
@@ -94,77 +219,272 @@ public abstract class AbstractList extends AbstractCollection implements List
     Iterator itr1 = iterator();
     Iterator itr2 = ((List) o).iterator();
 
-    for (int pos = 0; pos < size; pos++)
-      {
-       Object e = itr1.next();
-       if (e == null ? itr2.next() != null : !e.equals(itr2.next()))
-         return false;
-      }
+    while (--size >= 0)
+      if (! equals(itr1.next(), itr2.next()))
+        return false;
     return true;
   }
 
+  /**
+   * Obtain a hash code for this list. In order to obey the general contract of
+   * the hashCode method of class Object, this value is calculated as follows:
+   * <pre>
+   *   hashCode = 1;
+   *   Iterator i = list.iterator();
+   *   while (i.hasNext())
+   *     {
+   *       Object obj = i.next();
+   *       hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
+   *     }
+   * </pre>
+   * This ensures that the general contract of Object.hashCode() is adhered to.
+   *
+   * @return the hash code of this list
+   * @see Object#hashCode()
+   * @see #equals(Object)
+   */
   public int hashCode()
   {
     int hashCode = 1;
     Iterator itr = iterator();
-    int size = size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       Object obj = itr.next();
-       hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
-      }
+    int pos = size();
+    while (--pos >= 0)
+      hashCode = 31 * hashCode + hashCode(itr.next());
     return hashCode;
   }
 
+  /**
+   * Obtain the first index at which a given object is to be found in this
+   * list. This implementation follows a listIterator() until a match is found,
+   * or returns -1 if the list end is reached.
+   *
+   * @param o the object to search for
+   * @return the least integer n such that <code>o == null ? get(n) == null :
+   *         o.equals(get(n))</code>, or -1 if there is no such index
+   */
   public int indexOf(Object o)
   {
-    ListIterator itr = listIterator(0);
+    ListIterator itr = listIterator();
     int size = size();
     for (int pos = 0; pos < size; pos++)
-      {
-       if (o == null ? itr.next() == null : o.equals(itr.next()))
-         return pos;
-      }
+      if (equals(o, itr.next()))
+        return pos;
     return -1;
   }
 
+  /**
+   * Obtain an Iterator over this list, whose sequence is the list order.
+   * This implementation uses size(), get(int), and remove(int) of the
+   * backing list, and does not support remove unless the list does. This
+   * implementation is fail-fast if you correctly maintain modCount.
+   * Also, this implementation is specified by Sun to be distinct from
+   * listIterator, although you could easily implement it as
+   * <code>return listIterator(0)</code>.
+   *
+   * @return an Iterator over the elements of this list, in order
+   * @see #modCount
+   */
   public Iterator iterator()
   {
-    return new AbstractListItr(0);
+    // Bah, Sun's implementation forbids using listIterator(0).
+    return new Iterator()
+    {
+      private int pos = 0;
+      private int size = size();
+      private int last = -1;
+      private int knownMod = modCount;
+
+      // This will get inlined, since it is private.
+      private void checkMod()
+      {
+        if (knownMod != modCount)
+          throw new ConcurrentModificationException();
+      }
+
+      public boolean hasNext()
+      {
+        checkMod();
+        return pos < size;
+      }
+
+      public Object next()
+      {
+        checkMod();
+        if (pos == size)
+          throw new NoSuchElementException();
+        last = pos;
+        return get(pos++);
+      }
+
+      public void remove()
+      {
+        checkMod();
+        if (last < 0)
+          throw new IllegalStateException();
+        AbstractList.this.remove(last);
+        pos--;
+        size--;
+        last = -1;
+        knownMod = modCount;
+      }
+    };
   }
 
+  /**
+   * Obtain the last index at which a given object is to be found in this
+   * list. This implementation grabs listIterator(size()), then searches
+   * backwards for a match or returns -1.
+   *
+   * @return the greatest integer n such that <code>o == null ? get(n) == null
+   *         : o.equals(get(n))</code>, or -1 if there is no such index
+   */
   public int lastIndexOf(Object o)
   {
-    int size = size();
-    ListIterator itr = listIterator(size);
-    for (int pos = size; pos > 0; pos--)
-      {
-       if (o == null ? itr.previous() == null : o.equals(itr.previous()))
-         return pos - 1;
-      }
+    int pos = size();
+    ListIterator itr = listIterator(pos);
+    while (--pos >= 0)
+      if (equals(o, itr.previous()))
+        return pos;
     return -1;
   }
 
   /**
-   * Return an Iterator over this List. This implementation calls
-   * listIterator(0).
+   * Obtain a ListIterator over this list, starting at the beginning. This
+   * implementation returns listIterator(0).
    *
-   * @return an Iterator over this List
+   * @return a ListIterator over the elements of this list, in order, starting
+   *         at the beginning
    */
   public ListIterator listIterator()
   {
     return listIterator(0);
   }
 
-  public ListIterator listIterator(int index)
+  /**
+   * Obtain a ListIterator over this list, starting at a given position.
+   * A first call to next() would return the same as get(index), and a
+   * first call to previous() would return the same as get(index - 1).
+   * <p>
+   *
+   * This implementation uses size(), get(int), set(int, Object),
+   * add(int, Object), and remove(int) of the backing list, and does not
+   * support remove, set, or add unless the list does. This implementation
+   * is fail-fast if you correctly maintain modCount.
+   *
+   * @param index the position, between 0 and size() inclusive, to begin the
+   *        iteration from
+   * @return a ListIterator over the elements of this list, in order, starting
+   *         at index
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   * @see #modCount
+   */
+  public ListIterator listIterator(final int index)
   {
     if (index < 0 || index > size())
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size());
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+                                          size());
 
-    return new AbstractListItr(index);
+    return new ListIterator()
+    {
+      private int knownMod = modCount;
+      private int position = index;
+      private int lastReturned = -1;
+      private int size = size();
+
+      // This will get inlined, since it is private.
+      private void checkMod()
+      {
+        if (knownMod != modCount)
+          throw new ConcurrentModificationException();
+      }
+
+      public boolean hasNext()
+      {
+        checkMod();
+        return position < size;
+      }
+
+      public boolean hasPrevious()
+      {
+        checkMod();
+        return position > 0;
+      }
+
+      public Object next()
+      {
+        checkMod();
+        if (position == size)
+          throw new NoSuchElementException();
+        lastReturned = position;
+        return get(position++);
+      }
+
+      public Object previous()
+      {
+        checkMod();
+        if (position == 0)
+          throw new NoSuchElementException();
+        lastReturned = --position;
+        return get(lastReturned);
+      }
+
+      public int nextIndex()
+      {
+        checkMod();
+        return position;
+      }
+
+      public int previousIndex()
+      {
+        checkMod();
+        return position - 1;
+      }
+
+      public void remove()
+      {
+        checkMod();
+        if (lastReturned < 0)
+          throw new IllegalStateException();
+        AbstractList.this.remove(lastReturned);
+        size--;
+        position = lastReturned;
+        lastReturned = -1;
+        knownMod = modCount;
+      }
+
+      public void set(Object o)
+      {
+        checkMod();
+        if (lastReturned < 0)
+          throw new IllegalStateException();
+        AbstractList.this.set(lastReturned, o);
+      }
+
+      public void add(Object o)
+      {
+        checkMod();
+        AbstractList.this.add(position++, o);
+        size++;
+        lastReturned = -1;
+        knownMod = modCount;
+      }
+    };
   }
 
+  /**
+   * Remove the element at a given position in this list (optional operation).
+   * Shifts all remaining elements to the left to fill the gap. This
+   * implementation always throws an UnsupportedOperationException.
+   * If you want fail-fast iterators, be sure to increment modCount when
+   * overriding this.
+   *
+   * @param index the position within the list of the object to remove
+   * @return the object that was removed
+   * @throws UnsupportedOperationException if this list does not support the
+   *         remove operation
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   * @see #modCount
+   */
   public Object remove(int index)
   {
     throw new UnsupportedOperationException();
@@ -175,8 +495,10 @@ public abstract class AbstractList extends AbstractCollection implements List
    * removeRange methods of the class which implements subList, which are
    * difficult for subclasses to override directly. Therefore, this method
    * should be overridden instead by the more efficient implementation, if one
-   * exists.
+   * exists. Overriding this can reduce quadratic efforts to constant time
+   * in some cases!
    * <p>
+   *
    * This implementation first checks for illegal or out of range arguments. It
    * then obtains a ListIterator over the list using listIterator(fromIndex).
    * It then calls next() and remove() on this iterator repeatedly, toIndex -
@@ -190,152 +512,131 @@ public abstract class AbstractList extends AbstractCollection implements List
     ListIterator itr = listIterator(fromIndex);
     for (int index = fromIndex; index < toIndex; index++)
       {
-       itr.next();
-       itr.remove();
+        itr.next();
+        itr.remove();
       }
   }
 
+  /**
+   * Replace an element of this list with another object (optional operation).
+   * This implementation always throws an UnsupportedOperationException.
+   *
+   * @param index the position within this list of the element to be replaced
+   * @param o the object to replace it with
+   * @return the object that was replaced
+   * @throws UnsupportedOperationException if this list does not support the
+   *         set operation
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   * @throws ClassCastException if o cannot be added to this list due to its
+   *         type
+   * @throws IllegalArgumentException if o cannot be added to this list for
+   *         some other reason
+   */
   public Object set(int index, Object o)
   {
     throw new UnsupportedOperationException();
   }
 
+  /**
+   * Obtain a List view of a subsection of this list, from fromIndex
+   * (inclusive) to toIndex (exclusive). If the two indices are equal, the
+   * sublist is empty. The returned list should be modifiable if and only
+   * if this list is modifiable. Changes to the returned list should be
+   * reflected in this list. If this list is structurally modified in
+   * any way other than through the returned list, the result of any subsequent
+   * operations on the returned list is undefined.
+   * <p>
+   *
+   * This implementation returns a subclass of AbstractList. It stores, in
+   * private fields, the offset and size of the sublist, and the expected
+   * modCount of the backing list. If the backing list implements RandomAccess,
+   * the sublist will also.
+   * <p>
+   *
+   * The subclass's <code>set(int, Object)</code>, <code>get(int)</code>,
+   * <code>add(int, Object)</code>, <code>remove(int)</code>,
+   * <code>addAll(int, Collection)</code> and
+   * <code>removeRange(int, int)</code> methods all delegate to the
+   * corresponding methods on the backing abstract list, after
+   * bounds-checking the index and adjusting for the offset. The
+   * <code>addAll(Collection c)</code> method merely returns addAll(size, c).
+   * The <code>listIterator(int)</code> method returns a "wrapper object"
+   * over a list iterator on the backing list, which is created with the
+   * corresponding method on the backing list. The <code>iterator()</code>
+   * method merely returns listIterator(), and the <code>size()</code> method
+   * merely returns the subclass's size field.
+   * <p>
+   *
+   * All methods first check to see if the actual modCount of the backing
+   * list is equal to its expected value, and throw a
+   * ConcurrentModificationException if it is not. 
+   *
+   * @param fromIndex the index that the returned list should start from
+   *        (inclusive)
+   * @param toIndex the index that the returned list should go to (exclusive)
+   * @return a List backed by a subsection of this list
+   * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; size()
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @see ConcurrentModificationException
+   * @see RandomAccess
+   */
   public List subList(int fromIndex, int toIndex)
   {
+    // This follows the specification of AbstractList, but is inconsistent
+    // with the one in List. Don't you love Sun's inconsistencies?
     if (fromIndex > toIndex)
       throw new IllegalArgumentException(fromIndex + " > " + toIndex);
     if (fromIndex < 0 || toIndex > size())
       throw new IndexOutOfBoundsException();
 
+    if (this instanceof RandomAccess)
+      return new RandomAccessSubList(this, fromIndex, toIndex);
     return new SubList(this, fromIndex, toIndex);
   }
 
-  class AbstractListItr implements ListIterator
-  {
-    private int knownMod = modCount;
-    private int position;
-    private int lastReturned = -1;
-    private int size = size();
-
-    AbstractListItr(int start_pos)
-    {
-      this.position = start_pos;
-    }
-
-    private void checkMod()
-    {
-      if (knownMod != modCount)
-       throw new ConcurrentModificationException();
-    }
-
-    public boolean hasNext()
-    {
-      checkMod();
-      return position < size;
-    }
-
-    public boolean hasPrevious()
-    {
-      checkMod();
-      return position > 0;
-    }
-
-    public Object next()
-    {
-      checkMod();
-      if (position < size)
-       {
-         lastReturned = position++;
-         return get(lastReturned);
-       }
-      else
-       {
-         throw new NoSuchElementException();
-       }
-    }
-
-    public Object previous()
-    {
-      checkMod();
-      if (position > 0)
-       {
-         lastReturned = --position;
-         return get(lastReturned);
-       }
-      else
-       {
-         throw new NoSuchElementException();
-       }
-    }
-
-    public int nextIndex()
-    {
-      checkMod();
-      return position;
-    }
-
-    public int previousIndex()
-    {
-      checkMod();
-      return position - 1;
-    }
-
-    public void remove()
-    {
-      checkMod();
-      if (lastReturned < 0)
-       {
-         throw new IllegalStateException();
-       }
-      AbstractList.this.remove(lastReturned);
-      knownMod++;
-      position = lastReturned;
-      lastReturned = -1;
-    }
-
-    public void set(Object o)
-    {
-      checkMod();
-      if (lastReturned < 0)
-       throw new IllegalStateException();
-      AbstractList.this.set(lastReturned, o);
-    }
-
-    public void add(Object o)
-    {
-      checkMod();
-      AbstractList.this.add(position++, o);
-      lastReturned = -1;
-      knownMod++;
-    }
-  }                            // AbstractList.Iterator
-}
-
+} // class AbstractList
 
 
+/**
+ * This class follows the implementation requirements set forth in
+ * {@link AbstractList#subList(int, int)}. Some compilers have problems
+ * with AbstractList.this.modCount if this class is nested in AbstractList,
+ * even though the JLS defines that to be legal, so we make it a top-level
+ * class.
+ *
+ * @author Original author unknown
+ * @author Eric Blake <ebb9@email.byu.edu>
+ */
 class SubList extends AbstractList
 {
-  private AbstractList backingList;
-  private int offset;
+  private final AbstractList backingList;
+  private final int offset;
   private int size;
 
-  public SubList(AbstractList backing, int fromIndex, int toIndex)
+  /**
+   * Construct the sublist.
+   *
+   * @param backing the list this comes from
+   * @param fromIndex the lower bound, inclusive
+   * @param toIndex the upper bound, exclusive
+   */
+  SubList(AbstractList backing, int fromIndex, int toIndex)
   {
     backingList = backing;
-    modCount = backingList.modCount;
+    modCount = backing.modCount;
     offset = fromIndex;
     size = toIndex - fromIndex;
   }
 
   /**
    * This method checks the two modCount fields to ensure that there has
-   * not been a concurrent modification. It throws an exception if there
-   * has been, and otherwise returns normally.
-   * Note that since this method is private, it will be inlined.
+   * not been a concurrent modification, returning if all is okay.
    *
-   * @exception ConcurrentModificationException if there has been a
-   *   concurrent modification.
+   * @throws ConcurrentModificationException if the backing list has been
+   *         modified externally to this sublist
    */
+  // This will get inlined, since it is private.
   private void checkMod()
   {
     if (modCount != backingList.modCount)
@@ -345,45 +646,64 @@ class SubList extends AbstractList
   /**
    * This method checks that a value is between 0 and size (inclusive). If
    * it is not, an exception is thrown.
-   * Note that since this method is private, it will be inlined.
    *
-   * @exception IndexOutOfBoundsException if the value is out of range.
+   * @param index the value to check
+   * @throws IndexOutOfBoundsException if the value is out of range
    */
+  // This will get inlined, since it is private.
   private void checkBoundsInclusive(int index)
   {
     if (index < 0 || index > size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+                                          size);
   }
 
   /**
    * This method checks that a value is between 0 (inclusive) and size
    * (exclusive). If it is not, an exception is thrown.
-   * Note that since this method is private, it will be inlined.
    *
-   * @exception IndexOutOfBoundsException if the value is out of range.
+   * @param index the value to check
+   * @throws IndexOutOfBoundsException if the value is out of range
    */
+  // This will get inlined, since it is private.
   private void checkBoundsExclusive(int index)
   {
     if (index < 0 || index >= size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+                                          size);
   }
 
+  /**
+   * Specified by AbstractList.subList to return the private field size.
+   *
+   * @return the sublist size
+   */
   public int size()
   {
     checkMod();
     return size;
   }
 
+  /**
+   * Specified by AbstractList.subList to delegate to the backing list.
+   *
+   * @param index the location to modify
+   * @param o the new value
+   * @return the old value
+   */
   public Object set(int index, Object o)
   {
     checkMod();
     checkBoundsExclusive(index);
-    o = backingList.set(index + offset, o);
-    return o;
+    return backingList.set(index + offset, o);
   }
 
+  /**
+   * Specified by AbstractList.subList to delegate to the backing list.
+   *
+   * @param index the location to get from
+   * @return the object at that location
+   */
   public Object get(int index)
   {
     checkMod();
@@ -391,62 +711,109 @@ class SubList extends AbstractList
     return backingList.get(index + offset);
   }
 
+  /**
+   * Specified by AbstractList.subList to delegate to the backing list.
+   *
+   * @param index the index to insert at
+   * @param o the object to add
+   */
   public void add(int index, Object o)
   {
     checkMod();
     checkBoundsInclusive(index);
     backingList.add(index + offset, o);
-    this.modCount++;
     size++;
+    modCount = backingList.modCount;
   }
 
+  /**
+   * Specified by AbstractList.subList to delegate to the backing list.
+   *
+   * @param index the index to remove
+   * @return the removed object
+   */
   public Object remove(int index)
   {
     checkMod();
     checkBoundsExclusive(index);
     Object o = backingList.remove(index + offset);
-    this.modCount++;
     size--;
+    modCount = backingList.modCount;
     return o;
   }
 
-  public void removeRange(int fromIndex, int toIndex)
+  /**
+   * Specified by AbstractList.subList to delegate to the backing list.
+   * This does no bounds checking, as it assumes it will only be called
+   * by trusted code like clear() which has already checked the bounds.
+   *
+   * @param fromIndex the lower bound, inclusive
+   * @param toIndex the upper bound, exclusive
+   */
+  protected void removeRange(int fromIndex, int toIndex)
   {
     checkMod();
-    checkBoundsExclusive(fromIndex);
-    checkBoundsInclusive(toIndex);
 
-    // this call will catch the toIndex < fromIndex condition
     backingList.removeRange(offset + fromIndex, offset + toIndex);
-    this.modCount = backingList.modCount;
     size -= toIndex - fromIndex;
+    modCount = backingList.modCount;
   }
 
+  /**
+   * Specified by AbstractList.subList to delegate to the backing list.
+   *
+   * @param index the location to insert at
+   * @param c the collection to insert
+   * @return true if this list was modified, in other words, c is non-empty
+   */
   public boolean addAll(int index, Collection c)
   {
     checkMod();
     checkBoundsInclusive(index);
     int csize = c.size();
     boolean result = backingList.addAll(offset + index, c);
-    this.modCount = backingList.modCount;
     size += csize;
+    modCount = backingList.modCount;
     return result;
   }
 
+  /**
+   * Specified by AbstractList.subList to return addAll(size, c).
+   *
+   * @param c the collection to insert
+   * @return true if this list was modified, in other words, c is non-empty
+   */
+  public boolean addAll(Collection c)
+  {
+    return addAll(size, c);
+  }
+
+  /**
+   * Specified by AbstractList.subList to return listIterator().
+   *
+   * @return an iterator over the sublist
+   */
   public Iterator iterator()
   {
-    return listIterator(0);
+    return listIterator();
   }
 
+  /**
+   * Specified by AbstractList.subList to return a wrapper around the
+   * backing list's iterator.
+   *
+   * @param index the start location of the iterator
+   * @return a list iterator over the sublist
+   */
   public ListIterator listIterator(final int index)
-  {      
+  {
     checkMod();
     checkBoundsInclusive(index);
 
-    return new ListIterator() 
+    return new ListIterator()
     {
-      ListIterator i = backingList.listIterator(index + offset);
-      int position = index;
+      private final ListIterator i = backingList.listIterator(index + offset);
+      private int position = index;
 
       public boolean hasNext()
       {
@@ -462,44 +829,36 @@ class SubList extends AbstractList
 
       public Object next()
       {
-        if (position < size)
-         {
-            Object o = i.next();
-            position++;
-            return o;
-          }
-       else
+        if (position == size)
           throw new NoSuchElementException();
+        position++;
+        return i.next();
       }
 
       public Object previous()
       {
-        if (position > 0)
-         {
-            Object o = i.previous();
-            position--;
-            return o;
-          }
-       else
+        if (position == 0)
           throw new NoSuchElementException();
+        position--;
+        return i.previous();
       }
 
       public int nextIndex()
       {
-        return offset + i.nextIndex();
+        return i.nextIndex() - offset;
       }
 
       public int previousIndex()
       {
-        return offset + i.previousIndex();
+        return i.previousIndex() - offset;
       }
 
       public void remove()
       {
         i.remove();
-       modCount++;
         size--;
         position = nextIndex();
+        modCount = backingList.modCount;
       }
 
       public void set(Object o)
@@ -510,14 +869,14 @@ class SubList extends AbstractList
       public void add(Object o)
       {
         i.add(o);
-       modCount++;
         size++;
         position++;
+        modCount = backingList.modCount;
       }
 
       // Here is the reason why the various modCount fields are mostly
       // ignored in this wrapper listIterator.
-      // IF the backing listIterator is failfast, then the following holds:
+      // If the backing listIterator is failfast, then the following holds:
       //   Using any other method on this list will call a corresponding
       //   method on the backing list *after* the backing listIterator
       //   is created, which will in turn cause a ConcurrentModException
@@ -530,9 +889,31 @@ class SubList extends AbstractList
       //   only, but somewhat pointless when the list can be changed under
       //   us.
       // Either way, no explicit handling of modCount is needed.
-      // However modCount++ must be executed in add and remove, and size
-      // must also be updated in these two methods, since they do not go
-      // through the corresponding methods of the subList.
+      // However modCount = backingList.modCount must be executed in add
+      // and remove, and size must also be updated in these two methods,
+      // since they do not go through the corresponding methods of the subList.
     };
   }
-}  // SubList
+} // class SubList
+
+/**
+ * This class is a RandomAccess version of SubList, as required by
+ * {@link AbstractList#subList(int, int)}.
+ *
+ * @author Eric Blake <ebb9@email.byu.edu>
+ */
+final class RandomAccessSubList extends SubList
+  implements RandomAccess
+{
+  /**
+   * Construct the sublist.
+   *
+   * @param backing the list this comes from
+   * @param fromIndex the lower bound, inclusive
+   * @param toIndex the upper bound, exclusive
+   */
+  RandomAccessSubList(AbstractList backing, int fromIndex, int toIndex)
+  {
+    super(backing, fromIndex, toIndex);
+  }
+} // class RandomAccessSubList
index e28ce919beb8121d5c368b1f4bcb7325c16a4427..f8cf79f29b0d511457c759ccc007c8586fd0ee39 100644 (file)
@@ -1,5 +1,5 @@
 /* AbstractMap.java -- Abstract implementation of most of Map
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -25,22 +25,71 @@ This exception does not however invalidate any other reasons why the
 executable file might be covered by the GNU General Public License. */
 
 
-// TO DO:
-// comments
-// test suite
-
 package java.util;
 
+/**
+ * An abstract implementation of Map to make it easier to create your own
+ * implementations. In order to create an unmodifiable Map, subclass
+ * AbstractMap and implement the <code>entrySet</code> (usually via an
+ * AbstractSet).  To make it modifiable, also implement <code>put</code>,
+ * and have <code>entrySet().iterator()</code> support <code>remove</code>.
+ * <p>
+ *
+ * It is recommended that classes which extend this support at least the
+ * no-argument constructor, and a constructor which accepts another Map.
+ * Further methods in this class may be overridden if you have a more
+ * efficient implementation.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Map
+ * @see Collection
+ * @see HashMap
+ * @see LinkedHashMap
+ * @see TreeMap
+ * @see WeakHashMap
+ * @see IdentityHashMap
+ * @since 1.2
+ * @status updated to 1.4
+ */
 public abstract class AbstractMap implements Map
 {
+  /** An "enum" of iterator types. */
+  // Package visible for use by subclasses.
+  static final int KEYS = 0,
+                   VALUES = 1,
+                   ENTRIES = 2;
+
+  /**
+   * The cache for {@link #keySet()}.
+   */
+  // Package visible for use by subclasses.
+  Set keys;
+
+  /**
+   * The cache for {@link #values()}.
+   */
+  // Package visible for use by subclasses.
+  Collection values;
+
   /**
-   * Remove all entries from this Map. This default implementation calls
-   * entrySet().clear().
+   * The main constructor, for use by subclasses.
+   */
+  protected AbstractMap()
+  {
+  }
+
+  /**
+   * Remove all entries from this Map (optional operation). This default
+   * implementation calls entrySet().clear(). NOTE: If the entry set does
+   * not permit clearing, then this will fail, too. Subclasses often
+   * override this for efficiency.  Your implementation of entrySet() should
+   * not call <code>AbstractMap.clear</code> unless you want an infinite loop.
    *
-   * @throws UnsupportedOperationException
-   * @specnote The JCL book claims that this implementation always throws 
-   *           UnsupportedOperationException, while the online docs claim it
-   *           calls entrySet().clear(). We take the later to be correct.
+   * @throws UnsupportedOperationException if <code>entrySet().clear()</code>
+   *         does not support clearing.
+   * @see Set#clear()
    */
   public void clear()
   {
@@ -48,246 +97,414 @@ public abstract class AbstractMap implements Map
   }
 
   /**
-   * 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
+   * default implementation simply calls <code>super.clone()</code>.
+   *
+   * @return the shallow clone
+   * @throws CloneNotSupportedException if a subclass is not Cloneable
+   * @see Cloneable
+   * @see Object#clone()
    */
-  protected Object clone () throws CloneNotSupportedException
+  protected Object clone() throws CloneNotSupportedException
   {
-    return super.clone ();
+    AbstractMap copy = (AbstractMap) super.clone();
+    // Clear out the caches; they are stale.
+    copy.keys = null;
+    copy.values = null;
+    return copy;
   }
 
+  /**
+   * Returns true if this contains a mapping for the given key. This
+   * implementation does a linear search, O(n), over the
+   * <code>entrySet()</code>, returning <code>true</code> if a match
+   * is found, <code>false</code> if the iteration ends. Many subclasses
+   * can implement this more efficiently.
+   *
+   * @param key the key to search for
+   * @return true if the map contains the key
+   * @throws NullPointerException if key is <code>null</code> but the map
+   *         does not permit null keys
+   * @see #containsValue(Object)
+   */
   public boolean containsKey(Object key)
   {
-    Object k;
-    Set es = entrySet();
-    Iterator entries = es.iterator();
-    int size = size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       k = ((Map.Entry) entries.next()).getKey();
-       if (key == null ? k == null : key.equals(k))
-         return true;
-      }
+    Iterator entries = entrySet().iterator();
+    int pos = size();
+    while (--pos >= 0)
+      if (equals(key, ((Map.Entry) entries.next()).getKey()))
+        return true;
     return false;
   }
 
+  /**
+   * Returns true if this contains at least one mapping with the given value.
+   * This implementation does a linear search, O(n), over the
+   * <code>entrySet()</code>, returning <code>true</code> if a match
+   * is found, <code>false</code> if the iteration ends. A match is
+   * defined as <code>(value == null ? v == null : value.equals(v))</code>
+   * Subclasses are unlikely to implement this more efficiently.
+   *
+   * @param value the value to search for
+   * @return true if the map contains the value
+   * @see #containsKey(Object)
+   */
   public boolean containsValue(Object value)
   {
-    Object v;
-    Set es = entrySet();
-    Iterator entries = es.iterator();
-    int size = size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       v = ((Map.Entry) entries.next()).getValue();
-       if (value == null ? v == null : value.equals(v))
-         return true;
-      }
+    Iterator entries = entrySet().iterator();
+    int pos = size();
+    while (--pos >= 0)
+      if (equals(value, ((Map.Entry) entries.next()).getValue()))
+        return true;
     return false;
   }
 
+  /**
+   * Returns a set view of the mappings in this Map.  Each element in the
+   * set must be an implementation of Map.Entry.  The set is backed by
+   * the map, so that changes in one show up in the other.  Modifications
+   * made while an iterator is in progress cause undefined behavior.  If
+   * the set supports removal, these methods must be valid:
+   * <code>Iterator.remove</code>, <code>Set.remove</code>,
+   * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>.
+   * Element addition is not supported via this set.
+   *
+   * @return the entry set
+   * @see Map.Entry
+   */
   public abstract Set entrySet();
 
+  /**
+   * Compares the specified object with this map for equality. Returns
+   * <code>true</code> if the other object is a Map with the same mappings,
+   * that is,<br>
+   * <code>o instanceof Map && entrySet().equals(((Map) o).entrySet();</code>
+   *
+   * @param o the object to be compared
+   * @return true if the object equals this map
+   * @see Set#equals(Object)
+   */
   public boolean equals(Object o)
   {
-    if (o == this)
-      return true;
-    if (!(o instanceof Map))
-      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;
+    return (o == this ||
+            (o instanceof Map &&
+             entrySet().equals(((Map) o).entrySet())));
   }
 
+  /**
+   * Returns the value mapped by the given key. Returns <code>null</code> if
+   * there is no mapping.  However, in Maps that accept null values, you
+   * must rely on <code>containsKey</code> to determine if a mapping exists.
+   * This iteration takes linear time, searching entrySet().iterator() of
+   * the key.  Many implementations override this method.
+   *
+   * @param key the key to look up
+   * @return the value associated with the key, or null if key not in map
+   * @throws NullPointerException if this map does not accept null keys
+   * @see #containsKey(Object)
+   */
   public Object get(Object key)
   {
-    Set s = entrySet();
-    Iterator entries = s.iterator();
-    int size = size();
-
-    for (int pos = 0; pos < size; pos++)
+    Iterator entries = entrySet().iterator();
+    int pos = size();
+    while (--pos >= 0)
       {
-       Map.Entry entry = (Map.Entry) entries.next();
-       Object k = entry.getKey();
-       if (key == null ? k == null : key.equals(k))
-         return entry.getValue();
+        Map.Entry entry = (Map.Entry) entries.next();
+        if (equals(key, entry.getKey()))
+          return entry.getValue();
       }
-
     return null;
   }
 
+  /**
+   * Returns the hash code for this map. As defined in Map, this is the sum
+   * of all hashcodes for each Map.Entry object in entrySet, or basically
+   * entrySet().hashCode().
+   *
+   * @return the hash code
+   * @see Map.Entry#hashCode()
+   * @see Set#hashCode()
+   */
   public int hashCode()
   {
-    int hashcode = 0;
-    Iterator itr = entrySet().iterator();
-    int size = size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       hashcode += itr.next().hashCode();
-      }
-    return hashcode;
+    return entrySet().hashCode();
   }
 
+  /**
+   * Returns true if the map contains no mappings. This is implemented by
+   * <code>size() == 0</code>.
+   *
+   * @return true if the map is empty
+   * @see #size()
+   */
   public boolean isEmpty()
   {
     return size() == 0;
   }
 
+  /**
+   * Returns a set view of this map's keys. The set is backed by the map,
+   * so changes in one show up in the other. Modifications while an iteration
+   * is in progress produce undefined behavior. The set supports removal
+   * if entrySet() does, but does not support element addition.
+   * <p>
+   *
+   * This implementation creates an AbstractSet, where the iterator wraps
+   * the entrySet iterator, size defers to the Map's size, and contains
+   * defers to the Map's containsKey. The set is created on first use, and
+   * returned on subsequent uses, although since no synchronization occurs,
+   * there is a slight possibility of creating two sets.
+   *
+   * @return a Set view of the keys
+   * @see Set#iterator()
+   * @see #size()
+   * @see #containsKey(Object)
+   * @see #values()
+   */
   public Set keySet()
   {
-    if (this.keySet == null)
+    if (keys == null)
+      keys = new AbstractSet()
       {
-       this.keySet = new AbstractSet()
-       {
-         public int size()
-         {
-           return AbstractMap.this.size();
-         }
-
-         public boolean contains(Object key)
-         {
-           return AbstractMap.this.containsKey(key);
-         }
-
-         public Iterator iterator()
-         {
-           return new Iterator()
-           {
-             Iterator map_iterator = AbstractMap.this.entrySet().iterator();
-
-             public boolean hasNext()
-             {
-               return map_iterator.hasNext();
-             }
-
-             public Object next()
-             {
-               return ((Map.Entry) map_iterator.next()).getKey();
-             }
-
-             public void remove()
-             {
-               map_iterator.remove();
-             }
-           };
-         }
-       };
-      }
-
-    return this.keySet;
+        public int size()
+        {
+          return AbstractMap.this.size();
+        }
+
+        public boolean contains(Object key)
+        {
+          return containsKey(key);
+        }
+
+        public Iterator iterator()
+        {
+          return new Iterator()
+          {
+            private final Iterator map_iterator = entrySet().iterator();
+
+            public boolean hasNext()
+            {
+              return map_iterator.hasNext();
+            }
+
+            public Object next()
+            {
+              return ((Map.Entry) map_iterator.next()).getKey();
+            }
+
+            public void remove()
+            {
+              map_iterator.remove();
+            }
+          };
+        }
+      };
+    return keys;
   }
 
+  /**
+   * Associates the given key to the given value (optional operation). If the
+   * map already contains the key, its value is replaced. This implementation
+   * simply throws an UnsupportedOperationException. Be aware that in a map
+   * that permits <code>null</code> values, a null return does not always
+   * imply that the mapping was created.
+   *
+   * @param key the key to map
+   * @param value the value to be mapped
+   * @return the previous value of the key, or null if there was no mapping
+   * @throws UnsupportedOperationException if the operation is not supported
+   * @throws ClassCastException if the key or value is of the wrong type
+   * @throws IllegalArgumentException if something about this key or value
+   *         prevents it from existing in this map
+   * @throws NullPointerException if the map forbids null keys or values
+   * @see #containsKey(Object)
+   */
   public Object put(Object key, Object value)
   {
     throw new UnsupportedOperationException();
   }
 
+  /**
+   * Copies all entries of the given map to this one (optional operation). If
+   * the map already contains a key, its value is replaced. This implementation
+   * simply iterates over the map's entrySet(), calling <code>put</code>,
+   * so it is not supported if puts are not.
+   *
+   * @param m the mapping to load into this map
+   * @throws UnsupportedOperationException if the operation is not supported
+   * @throws ClassCastException if a key or value is of the wrong type
+   * @throws IllegalArgumentException if something about a key or value
+   *         prevents it from existing in this map
+   * @throws NullPointerException if the map forbids null keys or values, or
+   *         if <code>m</code> is null.
+   * @see #put(Object, Object)
+   */
   public void putAll(Map m)
   {
-    Map.Entry entry;
     Iterator entries = m.entrySet().iterator();
-    int size = m.size();
-
-    for (int pos = 0; pos < size; pos++)
+    int pos = size();
+    while (--pos >= 0)
       {
-       entry = (Map.Entry) entries.next();
-       put(entry.getKey(), entry.getValue());
+        Map.Entry entry = (Map.Entry) entries.next();
+        put(entry.getKey(), entry.getValue());
       }
   }
 
+  /**
+   * Removes the mapping for this key if present (optional operation). This
+   * implementation iterates over the entrySet searching for a matching
+   * key, at which point it calls the iterator's <code>remove</code> method.
+   * It returns the result of <code>getValue()</code> on the entry, if found,
+   * or null if no entry is found. Note that maps which permit null values
+   * may also return null if the key was removed.  If the entrySet does not
+   * support removal, this will also fail. This is O(n), so many
+   * implementations override it for efficiency.
+   *
+   * @param key the key to remove
+   * @return the value the key mapped to, or null if not present
+   * @throws UnsupportedOperationException if deletion is unsupported
+   * @see Iterator#remove()
+   */
   public Object remove(Object key)
   {
     Iterator entries = entrySet().iterator();
-    int size = size();
-
-    for (int pos = 0; pos < size; pos++)
+    int pos = size();
+    while (--pos >= 0)
       {
-       Map.Entry entry = (Map.Entry) entries.next();
-       Object k = entry.getKey();
-       if (key == null ? k == null : key.equals(k))
-         {
-           Object value = entry.getValue();
-           entries.remove();
-           return value;
-         }
+        Map.Entry entry = (Map.Entry) entries.next();
+        if (equals(key, entry.getKey()))
+          {
+            // Must get the value before we remove it from iterator.
+            Object r = entry.getValue();
+            entries.remove();
+            return r;
+          }
       }
-
     return null;
   }
 
+  /**
+   * Returns the number of key-value mappings in the map. If there are more
+   * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. This is
+   * implemented as <code>entrySet().size()</code>.
+   *
+   * @return the number of mappings
+   * @see Set#size()
+   */
   public int size()
   {
     return entrySet().size();
   }
 
+  /**
+   * Returns a String representation of this map. This is a listing of the
+   * map entries (which are specified in Map.Entry as being
+   * <code>getKey() + "=" + getValue()</code>), separated by a comma and
+   * space (", "), and surrounded by braces ('{' and '}'). This implementation
+   * uses a StringBuffer and iterates over the entrySet to build the String.
+   * Note that this can fail with an exception if underlying keys or
+   * values complete abruptly in toString().
+   *
+   * @return a String representation
+   * @see Map.Entry#toString()
+   */
   public String toString()
   {
     Iterator entries = entrySet().iterator();
-    int size = size();
     StringBuffer r = new StringBuffer("{");
-    for (int pos = 0; pos < size; pos++)
+    for (int pos = size(); pos > 0; pos--)
       {
-        // Append the toString value of the entries rather than calling 
-       // getKey/getValue. This is more efficient and it matches the JDK
-       // behaviour.
-       r.append(entries.next());       
-       if (pos < size - 1)
-         r.append(", ");
+        // Append the toString value of the entries rather than calling
+        // getKey/getValue. This is more efficient and it matches the JDK
+        // behaviour.
+        r.append(entries.next());
+        if (pos > 1)
+          r.append(", ");
       }
     r.append("}");
     return r.toString();
   }
 
+  /**
+   * Returns a collection or bag view of this map's values. The collection
+   * is backed by the map, so changes in one show up in the other.
+   * Modifications while an iteration is in progress produce undefined
+   * behavior. The collection supports removal if entrySet() does, but
+   * does not support element addition.
+   * <p>
+   *
+   * This implementation creates an AbstractCollection, where the iterator
+   * wraps the entrySet iterator, size defers to the Map's size, and contains
+   * defers to the Map's containsValue. The collection is created on first
+   * use, and returned on subsequent uses, although since no synchronization
+   * occurs, there is a slight possibility of creating two collections.
+   *
+   * @return a Collection view of the values
+   * @see Collection#iterator()
+   * @see #size()
+   * @see #containsValue(Object)
+   * @see #keySet()
+   */
   public Collection values()
   {
-    if (this.valueCollection == null)
+    if (values == null)
+      values = new AbstractCollection()
       {
-       this.valueCollection = new AbstractCollection()
-       {
-         public int size()
-         {
-           return AbstractMap.this.size();
-         }
-
-         public Iterator iterator()
-         {
-           return new Iterator()
-           {
-             Iterator map_iterator = AbstractMap.this.entrySet().iterator();
-
-             public boolean hasNext()
-             {
-               return map_iterator.hasNext();
-             }
-
-             public Object next()
-             {
-               return ((Map.Entry) map_iterator.next()).getValue();
-             }
-
-             public void remove()
-             {
-               map_iterator.remove();
-             }
-           };
-         }
-       };
-      }
+        public int size()
+        {
+          return AbstractMap.this.size();
+        }
+
+        public Iterator iterator()
+        {
+          return new Iterator()
+          {
+            private final Iterator map_iterator = entrySet().iterator();
+
+            public boolean hasNext()
+            {
+              return map_iterator.hasNext();
+            }
+
+            public Object next()
+            {
+              return ((Map.Entry) map_iterator.next()).getValue();
+            }
+
+            public void remove()
+            {
+              map_iterator.remove();
+            }
+          };
+        }
+      };
+    return values;
+  }
 
-    return this.valueCollection;
+  /**
+   * Compare two objects according to Collection semantics.
+   *
+   * @param o1 the first object
+   * @param o2 the second object
+   * @return o1 == null ? o2 == null : o1.equals(o2)
+   */
+  // Package visible for use throughout java.util.
+  // It may be inlined since it is final.
+  static final boolean equals(Object o1, Object o2)
+  {
+    return o1 == null ? o2 == null : o1.equals(o2);
   }
 
-  private Collection valueCollection = null;
-  private Set keySet = null;
+  /**
+   * Hash an object according to Collection semantics.
+   *
+   * @param o the object to hash
+   * @return o1 == null ? 0 : o1.hashCode()
+   */
+  // Package visible for use throughout java.util.
+  // It may be inlined since it is final.
+  static final int hashCode(Object o)
+  {
+    return o == null ? 0 : o.hashCode();
+  }
 }
index 81481be0466258f22a60589486fa96c0003386bd..2267b02e523fda4a746e59aacce136a5897e39b3 100644 (file)
@@ -1,5 +1,5 @@
 /* AbstractSequentialList.java -- List implementation for sequential access
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -25,100 +25,192 @@ This exception does not however invalidate any other reasons why the
 executable file might be covered by the GNU General Public License. */
 
 
-// TO DO:
-// ~ Lots of doc comments still missing.
-// ~ The class comment should include a description of what should be overridden
-//   to provide what features, as should the listIterator comment.
-
 package java.util;
 
 /**
  * Abstract superclass to make it easier to implement the List interface when
- * backed by a sequential-access store, such as a linked list.
+ * backed by a sequential-access store, such as a linked list. For random
+ * access data, use AbstractList. This class implements the random access
+ * methods (<code>get</code>, <code>set</code>, <code>add</code>, and
+ * <code>remove</code>) atop the list iterator, opposite of AbstractList's
+ * approach of implementing the iterator atop random access.
+ * <p>
+ *
+ * To implement a list, you need an implementation for <code>size()</code>
+ * and <code>listIterator</code>.  With just <code>hasNext</code>,
+ * <code>next</code>, <code>hasPrevious</code>, <code>previous</code>,
+ * <code>nextIndex</code>, and <code>previousIndex</code>, you have an
+ * unmodifiable list. For a modifiable one, add <code>set</code>, and for
+ * a variable-size list, add <code>add</code> and <code>remove</code>.
+ * <p>
+ *
+ * The programmer should provide a no-argument constructor, and one that
+ * accepts another Collection, as recommended by the Collection interface.
+ * Unfortunately, there is no way to enforce this in Java.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see List
+ * @see AbstractList
+ * @see AbstractCollection
+ * @see ListIterator
+ * @see LinkedList
+ * @since 1.2
+ * @status updated to 1.4
  */
 public abstract class AbstractSequentialList extends AbstractList
 {
+  /**
+   * The main constructor, for use by subclasses.
+   */
+  protected AbstractSequentialList()
+  {
+  }
+
   /**
    * Returns a ListIterator over the list, starting from position index.
    * Subclasses must provide an implementation of this method.
    *
-   * @exception IndexOutOfBoundsException if index < 0 || index > size()
+   * @param index the starting position of the list
+   * @return the list iterator
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
    */
   public abstract ListIterator listIterator(int index);
 
   /**
-   * Add an element to the list at a given index. This implementation obtains a
-   * ListIterator positioned at the specified index, and then adds the element
-   * using the ListIterator's add method.
+   * Insert an element into the list at a given position (optional operation).
+   * This shifts all existing elements from that position to the end one
+   * index to the right. This version of add has no return, since it is
+   * assumed to always succeed if there is no exception. This iteration
+   * uses listIterator(index).add(o).
    *
-   * @param index the position to add the element
-   * @param o the element to insert
-   * @exception IndexOutOfBoundsException if index < 0 || index > size()
-   * @exception UnsupportedOperationException if the iterator returned by
-   *   listIterator(index) does not support the add method.
+   * @param index the location to insert the item
+   * @param o the object to insert
+   * @throws UnsupportedOperationException if this list does not support the
+   *         add operation
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   * @throws ClassCastException if o cannot be added to this list due to its
+   *         type
+   * @throws IllegalArgumentException if o cannot be added to this list for
+   *         some other reason
    */
   public void add(int index, Object o)
   {
-    ListIterator i = listIterator(index);
-    i.add(o);
+    listIterator(index).add(o);
   }
 
   /**
-   * @specnote The spec in the JDK1.3 online docs is wrong. The implementation
-   *           should not call next() to skip over new elements as they are
-   *           added, because iterator.add() should add new elements BEFORE
-   *           the cursor.
+   * Insert the contents of a collection into the list at a given position
+   * (optional operation). Shift all elements at that position to the right
+   * by the number of elements inserted. This operation is undefined if
+   * this list is modified during the operation (for example, if you try
+   * to insert a list into itself).
+   * <p>
+   *
+   * This implementation grabs listIterator(index), then proceeds to use add
+   * for each element returned by c's iterator. Sun's online specs are wrong,
+   * claiming that this also calls next(): listIterator.add() correctly
+   * skips the added element.
+   *
+   * @param index the location to insert the collection
+   * @param c the collection to insert
+   * @return true if the list was modified by this action, that is, if c is
+   *         non-empty
+   * @throws UnsupportedOperationException if this list does not support the
+   *         addAll operation
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   * @throws ClassCastException if some element of c cannot be added to this
+   *         list due to its type
+   * @throws IllegalArgumentException if some element of c cannot be added
+   *         to this list for some other reason
+   * @throws NullPointerException if the specified collection is null
+   * @see #add(int, Object)
    */
   public boolean addAll(int index, Collection c)
   {
-    boolean modified = false;
     Iterator ci = c.iterator();
     int size = c.size();
     ListIterator i = listIterator(index);
-    for (int pos = 0; pos < size; pos++)
-      {
-       i.add(ci.next());
-      }
-    return (size > 0);
+    for (int pos = size; pos > 0; pos--)
+      i.add(ci.next());
+    return size > 0;
   }
 
+  /**
+   * Get the element at a given index in this list. This implementation
+   * returns listIterator(index).next().
+   *
+   * @param index the index of the element to be returned
+   * @return the element at index index in this list
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
   public Object get(int index)
   {
-    ListIterator i = listIterator(index);
-    if (index < 0 || index > size())
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size());
-    return i.next();
+    // This is a legal listIterator position, but an illegal get.
+    if (index == size())
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+                                          size());
+    return listIterator(index).next();
   }
 
   /**
-   * Return an Iterator over this List. This implementation returns
-   * listIterator().
+   * Obtain an Iterator over this list, whose sequence is the list order. This
+   * implementation returns listIterator().
    *
-   * @return an Iterator over this List
+   * @return an Iterator over the elements of this list, in order
    */
   public Iterator iterator()
   {
     return listIterator();
   }
 
+  /**
+   * Remove the element at a given position in this list (optional operation).
+   * Shifts all remaining elements to the left to fill the gap. This
+   * implementation uses listIterator(index) and ListIterator.remove().
+   *
+   * @param index the position within the list of the object to remove
+   * @return the object that was removed
+   * @throws UnsupportedOperationException if this list does not support the
+   *         remove operation
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
   public Object remove(int index)
   {
+    // This is a legal listIterator position, but an illegal remove.
+    if (index == size())
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+                                          + size());
     ListIterator i = listIterator(index);
-    if (index < 0 || index > size())
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size());
     Object removed = i.next();
     i.remove();
     return removed;
   }
 
+  /**
+   * Replace an element of this list with another object (optional operation).
+   * This implementation uses listIterator(index) and ListIterator.set(o).
+   *
+   * @param index the position within this list of the element to be replaced
+   * @param o the object to replace it with
+   * @return the object that was replaced
+   * @throws UnsupportedOperationException if this list does not support the
+   *         set operation
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   * @throws ClassCastException if o cannot be added to this list due to its
+   *         type
+   * @throws IllegalArgumentException if o cannot be added to this list for
+   *         some other reason
+   */
   public Object set(int index, Object o)
   {
+    // This is a legal listIterator position, but an illegal set.
+    if (index == size())
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+                                          + size());
     ListIterator i = listIterator(index);
-    if (index < 0 || index > size())
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size());
     Object old = i.next();
     i.set(o);
     return old;
index 6c3f2196190d57756202debd2aedd4d292da6f29..e45e47ea84d9c67abc24899e3a2dc22c21b8524b 100644 (file)
@@ -1,5 +1,5 @@
 /* AbstractSet.java -- Abstract implementation of most of Set
-   Copyright (C) 1998, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -35,9 +35,27 @@ package java.util;
  * on them - specifically, no element may be in the set more than once). This
  * class simply provides implementations of equals() and hashCode() to fulfil
  * the requirements placed on them by the Set interface.
+ *
+ * @author Original author unknown
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see AbstractCollection
+ * @see Set
+ * @see HashSet
+ * @see TreeSet
+ * @see LinkedHashSet
+ * @since 1.2
+ * @status updated to 1.4
  */
 public abstract class AbstractSet extends AbstractCollection implements Set
 {
+  /**
+   * The main constructor, for use by subclasses.
+   */
+  protected AbstractSet()
+  {
+  }
+
   /**
    * Tests whether the given object is equal to this Set. This implementation
    * first checks whether this set <em>is</em> the given object, and returns
@@ -50,12 +68,9 @@ public abstract class AbstractSet extends AbstractCollection implements Set
    */
   public boolean equals(Object o)
   {
-    if (o == this)
-      return true;
-    else if (o instanceof Set && ((Set) o).size() == size())
-      return containsAll((Collection) o);
-    else
-      return false;
+    return (o == this ||
+            (o instanceof Set && ((Set) o).size() == size()
+             && containsAll((Collection) o)));
   }
 
   /**
@@ -69,14 +84,45 @@ public abstract class AbstractSet extends AbstractCollection implements Set
   public int hashCode()
   {
     Iterator itr = iterator();
-    int size = size();
     int hash = 0;
-    for (int pos = 0; pos < size; pos++)
+    int pos = size();
+    while (--pos >= 0)
+      hash += hashCode(itr.next());
+    return hash;
+  }
+
+  /**
+   * Removes from this set all elements in the given collection (optional
+   * operation). This implementation uses <code>size()</code> to determine
+   * the smaller collection.  Then, if this set is smaller, it iterates
+   * over the set, calling Iterator.remove if the collection contains
+   * the element.  If this set is larger, it iterates over the collection,
+   * calling Set.remove for all elements in the collection. Note that
+   * this operation will fail if a remove methods is not supported.
+   *
+   * @param c the collection of elements to remove
+   * @return true if the set was modified as a result
+   * @throws UnsupportedOperationException if remove is not supported
+   * @throws NullPointerException if the collection is null
+   * @see AbstractCollection#remove(Object)
+   * @see Collection#contains(Object)
+   * @see Iterator#remove()
+   */
+  public boolean removeAll(Collection c)
+  {
+    int oldsize = size();
+    int count = c.size();
+    Iterator i;
+    if (oldsize < count)
       {
-       Object obj = itr.next();
-       if (obj != null)
-         hash += obj.hashCode();
+       for (i = iterator(), count = oldsize; count > 0; count--)
+          if (c.contains(i.next()))
+            i.remove();
       }
-    return hash;
+    else
+      for (i = c.iterator(); count > 0; count--)
+        remove(i.next());
+    return oldsize != size();
   }
+
 }
index d6b663414a43259833199b7cc9859fa5bde93d61..3c75e56672fe550b74b5d1a3e85c836695e54910 100644 (file)
@@ -1,6 +1,6 @@
 /* ArrayList.java -- JDK1.2's answer to Vector; this is an array-backed
    implementation of the List interface
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -35,383 +35,535 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 /**
- * An array-backed implementation of the List interface.  ArrayList
- * performs well on simple tasks:  random access into a list, appending
- * to or removing from the end of a list, checking the size, &c.
+ * An array-backed implementation of the List interface.  This implements
+ * all optional list operations, and permits null elements, so that it is
+ * better than Vector, which it replaces. Random access is roughly constant
+ * time, and iteration is roughly linear time, so it is nice and fast, with
+ * less overhead than a LinkedList.
+ * <p>
  *
- * @author        Jon A. Zeppieri
- * @see           java.util.AbstractList
- * @see           java.util.List
+ * Each list has a capacity, and as the array reaches that capacity it
+ * is automatically transferred to a larger array. You also have access to
+ * ensureCapacity and trimToSize to control the backing array's size, avoiding
+ * reallocation or wasted memory.
+ * <p>
+ *
+ * ArrayList is not synchronized, so if you need multi-threaded access,
+ * consider using:<br>
+ * <code>List l = Collections.synchronizedList(new ArrayList(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Jon A. Zeppieri
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see List
+ * @see LinkedList
+ * @see Vector
+ * @see Collections#synchronizedList(List)
+ * @see AbstractList
+ * @status updated to 1.4
  */
 public class ArrayList extends AbstractList
-  implements List, Cloneable, Serializable
+  implements List, RandomAccess, Cloneable, Serializable
 {
-  /** the default capacity for new ArrayLists */
+  /**
+   * Compatible with JDK 1.2
+   */
+  private static final long serialVersionUID = 8683452581122892189L;
+
+  /**
+   * The default capacity for new ArrayLists.
+   */
   private static final int DEFAULT_CAPACITY = 16;
 
-  /** the number of elements in this list */
-  int size;
+  /**
+   * The number of elements in this list.
+   * @serial the list size
+   */
+  private int size;
 
-  /** where the data is stored */
-  transient Object[] data;
+  /**
+   * Where the data is stored.
+   */
+  private transient Object[] data;
 
-  /** 
-   * Construct a new ArrayList with the supplied initial capacity. 
+  /**
+   * Construct a new ArrayList with the supplied initial capacity.
    *
-   * @param capacity Initial capacity of this ArrayList
+   * @param capacity initial capacity of this ArrayList
+   * @throws IllegalArgumentException if capacity is negative
    */
   public ArrayList(int capacity)
   {
+    // Must explicitly check, to get correct exception.
+    if (capacity < 0)
+      throw new IllegalArgumentException();
     data = new Object[capacity];
   }
 
-
   /**
-   * Construct a new ArrayList with the default capcity 
+   * Construct a new ArrayList with the default capcity (16).
    */
   public ArrayList()
   {
     this(DEFAULT_CAPACITY);
   }
 
-  /** 
+  /**
    * Construct a new ArrayList, and initialize it with the elements
-   * in the supplied Collection; Sun specs say that the initial 
-   * capacity is 110% of the Collection's size.
+   * in the supplied Collection. The initial capacity is 110% of the
+   * Collection's size.
    *
    * @param c the collection whose elements will initialize this list
+   * @throws NullPointerException if c is null
    */
   public ArrayList(Collection c)
   {
-    this((int) (c.size() * 1.1));
+    this((int) (c.size() * 1.1f));
     addAll(c);
   }
 
+  /**
+   * Trims the capacity of this List to be equal to its size;
+   * a memory saver.
+   */
+  public void trimToSize()
+  {
+    // Not a structural change from the perspective of iterators on this list,
+    // so don't update modCount.
+    if (size != data.length)
+      {
+        Object[] newData = new Object[size];
+        System.arraycopy(data, 0, newData, 0, size);
+        data = newData;
+      }
+  }
+
   /**
    * Guarantees that this list will have at least enough capacity to
-   * hold minCapacity elements. 
+   * hold minCapacity elements. This implementation will grow the list to
+   * max(current * 2, minCapacity) if (minCapacity > current). The JCL says
+   * explictly that "this method increases its capacity to minCap", while
+   * the JDK 1.3 online docs specify that the list will grow to at least the
+   * size specified.
    *
-   * @specnote This implementation will grow the list to 
-   *   max(current * 2, minCapacity) if (minCapacity > current). The JCL says
-   *   explictly that "this method increases its capacity to minCap", while
-   *   the JDK 1.3 online docs specify that the list will grow to at least the
-   *   size specified.
    * @param minCapacity the minimum guaranteed capacity
    */
   public void ensureCapacity(int minCapacity)
   {
-    Object[] newData;
     int current = data.length;
 
     if (minCapacity > current)
       {
-       newData = new Object[Math.max((current * 2), minCapacity)];
-       System.arraycopy(data, 0, newData, 0, size);
-       data = newData;
+        Object[] newData = new Object[Math.max(current * 2, minCapacity)];
+        System.arraycopy(data, 0, newData, 0, size);
+        data = newData;
       }
   }
 
   /**
-   * Appends the supplied element to the end of this list.
+   * Returns the number of elements in this list.
    *
-   * @param       e      the element to be appended to this list
+   * @return the list size
    */
-  public boolean add(Object e)
+  public int size()
   {
-    modCount++;
-    if (size == data.length)
-      ensureCapacity(size + 1);
-    data[size++] = e;
-    return true;
+    return size;
   }
 
   /**
-   * Retrieves the element at the user-supplied index.
+   * Checks if the list is empty.
    *
-   * @param    index        the index of the element we are fetching
-   * @throws   IndexOutOfBoundsException  (iIndex < 0) || (iIndex >= size())
+   * @return true if there are no elements
    */
-  public Object get(int index)
+  public boolean isEmpty()
   {
-    if (index < 0 || index >= size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
-    return data[index];
+    return size == 0;
   }
 
   /**
-   * Returns the number of elements in this list 
+   * Returns true iff element is in this ArrayList.
+   *
+   * @param e the element whose inclusion in the List is being tested
+   * @return true if the list contains e
    */
-  public int size()
+  public boolean contains(Object e)
   {
-    return size;
+    return indexOf(e) != -1;
   }
 
   /**
-   * Removes the element at the user-supplied index
+   * Returns the lowest index at which element appears in this List, or
+   * -1 if it does not appear.
    *
-   * @param     iIndex      the index of the element to be removed
-   * @return    the removed Object
-   * @throws    IndexOutOfBoundsException  (iIndex < 0) || (iIndex >= size())
+   * @param e the element whose inclusion in the List is being tested
+   * @return the index where e was found
    */
-  public Object remove(int index)
+  public int indexOf(Object e)
   {
-    modCount++;
-    if (index < 0 || index > size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
-    Object r = data[index];
-    if (index != --size)
-      System.arraycopy(data, (index + 1), data, index, (size - index));
-    data[size] = null;
-    return r;
+    for (int i = 0; i < size; i++)
+      if (equals(e, data[i]))
+        return i;
+    return -1;
   }
 
   /**
-   * Removes all elements in the half-open interval [iFromIndex, iToIndex).
+   * Returns the highest index at which element appears in this List, or
+   * -1 if it does not appear.
    *
-   * @param     fromIndex   the first index which will be removed
-   * @param     toIndex     one greater than the last index which will be 
-   *                         removed
+   * @param e the element whose inclusion in the List is being tested
+   * @return the index where e was found
    */
-  protected void removeRange(int fromIndex, int toIndex)
+  public int lastIndexOf(Object e)
   {
-    modCount++;
-    if (fromIndex != toIndex)
+    for (int i = size - 1; i >= 0; i--)
+      if (equals(e, data[i]))
+        return i;
+    return -1;
+  }
+
+  /**
+   * Creates a shallow copy of this ArrayList (elements are not cloned).
+   *
+   * @return the cloned object
+   */
+  public Object clone()
+  {
+    ArrayList clone = null;
+    try
+      {
+        clone = (ArrayList) super.clone();
+        clone.data = (Object[]) data.clone();
+      }
+    catch (CloneNotSupportedException e)
       {
-       System.arraycopy(data, toIndex, data, fromIndex, size - toIndex);
-       size -= (toIndex - fromIndex);
+        // Impossible to get here.
       }
+    return clone;
+  }
+
+  /**
+   * Returns an Object array containing all of the elements in this ArrayList.
+   * The array is independent of this list.
+   *
+   * @return an array representation of this list
+   */
+  public Object[] toArray()
+  {
+    Object[] array = new Object[size];
+    System.arraycopy(data, 0, array, 0, size);
+    return array;
+  }
+
+  /**
+   * Returns an Array whose component type is the runtime component type of
+   * the passed-in Array.  The returned Array is populated with all of the
+   * elements in this ArrayList.  If the passed-in Array is not large enough
+   * to store all of the elements in this List, a new Array will be created
+   * and returned; if the passed-in Array is <i>larger</i> than the size
+   * of this List, then size() index will be set to null.
+   *
+   * @param a the passed-in Array
+   * @return an array representation of this list
+   * @throws ArrayStoreException if the runtime type of a does not allow
+   *         an element in this list
+   * @throws NullPointerException if a is null
+   */
+  public Object[] toArray(Object[] a)
+  {
+    if (a.length < size)
+      a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
+                                       size);
+    else if (a.length > size)
+      a[size] = null;
+    System.arraycopy(data, 0, a, 0, size);
+    return a;
+  }
+
+  /**
+   * Retrieves the element at the user-supplied index.
+   *
+   * @param index the index of the element we are fetching
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
+  public Object get(int index)
+  {
+    checkBoundExclusive(index);
+    return data[index];
+  }
+
+  /**
+   * Sets the element at the specified index.
+   *
+   * @param index the index at which the element is being set
+   * @param e the element to be set
+   * @return the element previously at the specified index
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= 0
+   */
+  public Object set(int index, Object e)
+  {
+    checkBoundExclusive(index);
+    Object result = data[index];
+    data[index] = e;
+    return result;
+  }
+
+  /**
+   * Appends the supplied element to the end of this list.
+   *
+   * @param e the element to be appended to this list
+   * @return true, the add will always succeed
+   */
+  public boolean add(Object e)
+  {
+    modCount++;
+    if (size == data.length)
+      ensureCapacity(size + 1);
+    data[size++] = e;
+    return true;
   }
 
   /**
    * Adds the supplied element at the specified index, shifting all
    * elements currently at that index or higher one to the right.
    *
-   * @param     index      the index at which the element is being added
-   * @param     e          the item being added
+   * @param index the index at which the element is being added
+   * @param e the item being added
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
    */
   public void add(int index, Object e)
   {
+    checkBoundInclusive(index);
     modCount++;
-    if (index < 0 || index > size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
     if (size == data.length)
       ensureCapacity(size + 1);
     if (index != size)
-      System.arraycopy(data, index, data, index + 1, size - index);    
+      System.arraycopy(data, index, data, index + 1, size - index);
     data[index] = e;
     size++;
   }
 
-  /** 
-   * Add each element in the supplied Collection to this List.
+  /**
+   * Removes the element at the user-supplied index.
+   *
+   * @param index the index of the element to be removed
+   * @return the removed Object
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
+  public Object remove(int index)
+  {
+    checkBoundExclusive(index);
+    Object r = data[index];
+    modCount++;
+    if (index != --size)
+      System.arraycopy(data, index + 1, data, index, size - index);
+    // Aid for garbage collection by releasing this pointer.
+    data[size] = null;
+    return r;
+  }
+
+  /**
+   * Removes all elements from this List
+   */
+  public void clear()
+  {
+    if (size > 0)
+      {
+        modCount++;
+        // Allow for garbage collection.
+        Arrays.fill(data, 0, size, null);
+        size = 0;
+      }
+  }
+
+  /**
+   * Add each element in the supplied Collection to this List. It is undefined
+   * what happens if you modify the list while this is taking place; for
+   * example, if the collection contains this list.
    *
-   * @param        c          a Collection containing elements to be 
-   *                          added to this List
+   * @param c a Collection containing elements to be added to this List
+   * @return true if the list was modified, in other words c is not empty
+   * @throws NullPointerException if c is null
    */
   public boolean addAll(Collection c)
   {
     return addAll(size, c);
   }
 
-  /** 
+  /**
    * Add all elements in the supplied collection, inserting them beginning
    * at the specified index.
    *
-   * @param     index       the index at which the elements will be inserted
-   * @param     c           the Collection containing the elements to be
-   *                        inserted
+   * @param index the index at which the elements will be inserted
+   * @param c the Collection containing the elements to be inserted
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; 0
+   * @throws NullPointerException if c is null
    */
   public boolean addAll(int index, Collection c)
   {
-    if (index < 0 || index > size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
-    modCount++;
+    checkBoundInclusive(index);
     Iterator itr = c.iterator();
     int csize = c.size();
 
+    modCount++;
     if (csize + size > data.length)
       ensureCapacity(size + csize);
     int end = index + csize;
-    if (size > 0 && index != size)
+    if (index != size)
       System.arraycopy(data, index, data, end, csize);
     size += csize;
-    for (; index < end; index++)
-      {
-        data[index] = itr.next();
-      }
-    return (csize > 0);
+    for ( ; index < end; index++)
+      data[index] = itr.next();
+    return csize > 0;
   }
 
   /**
-   * Creates a shallow copy of this ArrayList
+   * Removes all elements in the half-open interval [fromIndex, toIndex).
+   * You asked for it if you call this with invalid arguments.
+   *
+   * @param fromIndex the first index which will be removed
+   * @param toIndex one greater than the last index which will be removed
    */
-  public Object clone()
+  protected void removeRange(int fromIndex, int toIndex)
   {
-    ArrayList clone = null;
-    try
+    if (fromIndex != toIndex)
       {
-       clone = (ArrayList) super.clone();
-       clone.data = new Object[data.length];
-       System.arraycopy(data, 0, clone.data, 0, size);
+        modCount++;
+        System.arraycopy(data, toIndex, data, fromIndex, size - toIndex);
+        size -= toIndex - fromIndex;
       }
-    catch (CloneNotSupportedException e) {}
-    return clone;
   }
 
-  /** 
-   * Returns true iff oElement is in this ArrayList.
+  /**
+   * Checks that the index is in the range of possible elements (inclusive).
    *
-   * @param     e     the element whose inclusion in the List is being
-   *                  tested
+   * @param index the index to check
+   * @throws IndexOutOfBoundsException if index &gt; size
    */
-  public boolean contains(Object e)
+  private void checkBoundInclusive(int index)
   {
-    return (indexOf(e) != -1);
+    // Implementation note: we do not check for negative ranges here, since
+    // use of a negative index will cause an ArrayIndexOutOfBoundsException,
+    // a subclass of the required exception, with no effort on our part.
+    if (index > size)
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
+                                          + size);
   }
 
   /**
-   * Returns the lowest index at which oElement appears in this List, or 
-   * -1 if it does not appear.
+   * Checks that the index is in the range of existing elements (exclusive).
    *
-   * @param    e       the element whose inclusion in the List is being
-   *                   tested
+   * @param index the index to check
+   * @throws IndexOutOfBoundsException if index &gt;= size
    */
-  public int indexOf(Object e)
+  private void checkBoundExclusive(int index)
   {
-    for (int i = 0; i < size; i++)
-      {
-       if (e == null ? data[i] == null : e.equals(data[i]))
-         return i;
-      }
-    return -1;
+    // Implementation note: we do not check for negative ranges here, since
+    // use of a negative index will cause an ArrayIndexOutOfBoundsException,
+    // a subclass of the required exception, with no effort on our part.
+    if (index >= size)
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
+                                          + size);
   }
 
   /**
-   * Returns the highest index at which oElement appears in this List, or 
-   * -1 if it does not appear.
+   * Remove from this list all elements contained in the given collection.
+   * This is not public, due to Sun's API, but this performs in linear
+   * time while the default behavior of AbstractList would be quadratic.
    *
-   * @param    e       the element whose inclusion in the List is being
-   *                   tested
+   * @param c the collection to filter out
+   * @return true if this list changed
+   * @throws NullPointerException if c is null
    */
-  public int lastIndexOf(Object e)
+  boolean removeAllInternal(Collection c)
   {
     int i;
+    int j;
+    for (i = 0; i < size; i++)
+      if (c.contains(data[i]))
+        break;
+    if (i == size)
+      return false;
 
-    for (i = size - 1; i >= 0; i--)
-      {
-       if (e == null ? data[i] == null : e.equals(data[i]))
-         return i;
-      }
-    return -1;
-  }
-
-  /**
-   * Removes all elements from this List
-   */
-  public void clear()
-  {
     modCount++;
-    for (int i = 0; i < size; i++)
-      {
-       data[i] = null;
-      }    
-    size = 0;
+    for (j = i++; i < size; i++)
+      if (! c.contains(data[i]))
+        data[j++] = data[i];
+    size -= i - j;
+    return true;
   }
 
   /**
-   * Sets the element at the specified index.
+   * Retain in this vector only the elements contained in the given collection.
+   * This is not public, due to Sun's API, but this performs in linear
+   * time while the default behavior of AbstractList would be quadratic.
    *
-   * @param     index   the index at which the element is being set
-   * @param     e       the element to be set
-   * @return    the element previously at the specified index, or null if
-   *            none was there
+   * @param c the collection to filter by
+   * @return true if this vector changed
+   * @throws NullPointerException if c is null
+   * @since 1.2
    */
-  public Object set(int index, Object e)
+  boolean retainAllInternal(Collection c)
   {
-    Object result;
-    if (index < 0 || index >= size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
-    result = data[index];
-    // SEH: no structural change, so don't update modCount
-    data[index] = e;
-    return result;
-  }
+    int i;
+    int j;
+    for (i = 0; i < size; i++)
+      if (! c.contains(data[i]))
+        break;
+    if (i == size)
+      return false;
 
-  /**
-   * Returns an Object Array containing all of the elements in this ArrayList
-   */
-  public Object[] toArray()
-  {
-    Object[] array = new Object[size];
-    System.arraycopy(data, 0, array, 0, size);
-    return array;
+    modCount++;
+    for (j = i++; i < size; i++)
+      if (c.contains(data[i]))
+        data[j++] = data[i];
+    size -= i - j;
+    return true;
   }
 
   /**
-   * Returns an Array whose component type is the runtime component type of
-   * the passed-in Array.  The returned Array is populated with all of the
-   * elements in this ArrayList.  If the passed-in Array is not large enough
-   * to store all of the elements in this List, a new Array will be created 
-   * and returned; if the passed-in Array is <i>larger</i> than the size
-   * of this List, then size() index will be set to null.
+   * Serializes this object to the given stream.
    *
-   * @param      array      the passed-in Array
+   * @param out the stream to write to
+   * @throws IOException if the underlying stream fails
+   * @serialData the size field (int), the length of the backing array
+   *             (int), followed by its elements (Objects) in proper order.
    */
-  public Object[] toArray(Object[] array)
+  private void writeObject(ObjectOutputStream s) throws IOException
   {
-    if (array.length < size)
-      array = (Object[]) Array.newInstance(array.getClass().getComponentType(), 
-                                          size);
-    else if (array.length > size)
-      array[size] = null;
-    System.arraycopy(data, 0, array, 0, size);
-    return array;
+    // The 'size' field.
+    s.defaultWriteObject();
+    // We serialize unused list entries to preserve capacity.
+    int len = data.length;
+    s.writeInt(len);
+    for (int i = 0; i < len; i++)
+      s.writeObject(data[i]);
   }
 
   /**
-   * Trims the capacity of this List to be equal to its size; 
-   * a memory saver.   
+   * Deserializes this object from the given stream.
+   *
+   * @param in the stream to read from
+   * @throws ClassNotFoundException if the underlying stream fails
+   * @throws IOException if the underlying stream fails
+   * @serialData the size field (int), the length of the backing array
+   *             (int), followed by its elements (Objects) in proper order.
    */
-  public void trimToSize()
-  {
-    // not a structural change from the perspective of iterators on this list, 
-    // so don't update modCount
-    Object[] newData = new Object[size];
-    System.arraycopy(data, 0, newData, 0, size);
-    data = newData;
-  }
-
-  private void writeObject(ObjectOutputStream out) throws IOException
-  {
-    int i;
-
-    // The 'size' field.
-    out.defaultWriteObject();
-
-    // FIXME: Do we really want to serialize unused list entries??
-    out.writeInt(data.length);
-    for (i = 0; i < data.length; i++)
-      out.writeObject(data[i]);
-  }
-
-  private void readObject(ObjectInputStream in)
+  private void readObject(ObjectInputStream s)
     throws IOException, ClassNotFoundException
   {
-    int i;
-    int capacity;
-
     // the `size' field.
-    in.defaultReadObject();
-
-    capacity = in.readInt();
+    s.defaultReadObject();
+    int capacity = s.readInt();
     data = new Object[capacity];
-
-    for (i = 0; i < capacity; i++)
-      data[i] = in.readObject();
+    for (int i = 0; i < capacity; i++)
+      data[i] = s.readObject();
   }
 }
index 87a40e3554746e149ae4ac280a694e8777be959b..d52431b7b3f88f601384da073818ec628d188608 100644 (file)
@@ -1,5 +1,5 @@
 /* Arrays.java -- Utility class with methods to operate on arrays
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -25,16 +25,33 @@ This exception does not however invalidate any other reasons why the
 executable file might be covered by the GNU General Public License. */
 
 
-// TO DO:
-// ~ Fix the behaviour of sort and binarySearch as applied to float and double
-//   arrays containing NaN values. See the JDC, bug ID 4143272.
-
 package java.util;
 
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
 /**
  * This class contains various static utility methods performing operations on
  * arrays, and a method to provide a List "view" of an array to facilitate
- * using arrays with Collection-based APIs.
+ * using arrays with Collection-based APIs. All methods throw a
+ * {@link NullPointerException} if the parameter array is null.
+ * <p>
+ *
+ * Implementations may use their own algorithms, but must obey the general
+ * properties; for example, the sort must be stable and n*log(n) complexity.
+ * Sun's implementation of sort, and therefore ours, is a tuned quicksort,
+ * adapted from Jon L. Bentley and M. Douglas McIlroy's "Engineering a Sort
+ * Function", Software-Practice and Experience, Vol. 23(11) P. 1249-1265
+ * (November 1993). This algorithm offers n*log(n) performance on many data
+ * sets that cause other quicksorts to degrade to quadratic performance.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Comparable
+ * @see Comparator
+ * @since 1.2
+ * @status updated to 1.4
  */
 public class Arrays
 {
@@ -45,14 +62,8 @@ public class Arrays
   {
   }
 
-  private static Comparator defaultComparator = new Comparator()
-  {
-    public int compare(Object o1, Object o2)
-    {
-      return ((Comparable) o1).compareTo(o2);
-    }
-  };
-
+\f
+// binarySearch
   /**
    * Perform a binary search of a byte array for a key. The array must be
    * sorted (as by the sort() method) - if it is not, the behaviour of this
@@ -63,32 +74,26 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
    */
-  public static int binarySearch(byte[]a, byte key)
+  public static int binarySearch(byte[] a, byte key)
   {
     int low = 0;
     int hi = a.length - 1;
     int mid = 0;
     while (low <= hi)
       {
-       mid = (low + hi) >> 1;
-       final byte d = a[mid];
-       if (d == key)
-         {
-           return mid;
-         }
-       else if (d > key)
-         {
-           hi = mid - 1;
-         }
-       else
-         {
-           // This gets the insertion point right on the last loop
-           low = ++mid;
-         }
+        mid = (low + hi) >> 1;
+        final byte d = a[mid];
+        if (d == key)
+          return mid;
+        else if (d > key)
+          hi = mid - 1;
+        else
+          // This gets the insertion point right on the last loop.
+          low = ++mid;
       }
     return -mid - 1;
   }
@@ -103,38 +108,32 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
    */
-  public static int binarySearch(char[]a, char key)
+  public static int binarySearch(char[] a, char key)
   {
     int low = 0;
     int hi = a.length - 1;
     int mid = 0;
     while (low <= hi)
       {
-       mid = (low + hi) >> 1;
-       final char d = a[mid];
-       if (d == key)
-         {
-           return mid;
-         }
-       else if (d > key)
-         {
-           hi = mid - 1;
-         }
-       else
-         {
-           // This gets the insertion point right on the last loop
-           low = ++mid;
-         }
+        mid = (low + hi) >> 1;
+        final char d = a[mid];
+        if (d == key)
+          return mid;
+        else if (d > key)
+          hi = mid - 1;
+        else
+          // This gets the insertion point right on the last loop.
+          low = ++mid;
       }
     return -mid - 1;
   }
 
   /**
-   * Perform a binary search of a double array for a key. The array must be
+   * Perform a binary search of a short array for a key. The array must be
    * sorted (as by the sort() method) - if it is not, the behaviour of this
    * method is undefined, and may be an infinite loop. If the array contains
    * the key more than once, any one of them may be found. Note: although the
@@ -143,38 +142,32 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
    */
-  public static int binarySearch(double[]a, double key)
+  public static int binarySearch(short[] a, short key)
   {
     int low = 0;
     int hi = a.length - 1;
     int mid = 0;
     while (low <= hi)
       {
-       mid = (low + hi) >> 1;
-       final double d = a[mid];
-       if (d == key)
-         {
-           return mid;
-         }
-       else if (d > key)
-         {
-           hi = mid - 1;
-         }
-       else
-         {
-           // This gets the insertion point right on the last loop
-           low = ++mid;
-         }
+        mid = (low + hi) >> 1;
+        final short d = a[mid];
+        if (d == key)
+          return mid;
+        else if (d > key)
+          hi = mid - 1;
+        else
+          // This gets the insertion point right on the last loop.
+          low = ++mid;
       }
     return -mid - 1;
   }
 
   /**
-   * Perform a binary search of a float array for a key. The array must be
+   * Perform a binary search of an int array for a key. The array must be
    * sorted (as by the sort() method) - if it is not, the behaviour of this
    * method is undefined, and may be an infinite loop. If the array contains
    * the key more than once, any one of them may be found. Note: although the
@@ -183,38 +176,32 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
    */
-  public static int binarySearch(float[]a, float key)
+  public static int binarySearch(int[] a, int key)
   {
     int low = 0;
     int hi = a.length - 1;
     int mid = 0;
     while (low <= hi)
       {
-       mid = (low + hi) >> 1;
-       final float d = a[mid];
-       if (d == key)
-         {
-           return mid;
-         }
-       else if (d > key)
-         {
-           hi = mid - 1;
-         }
-       else
-         {
-           // This gets the insertion point right on the last loop
-           low = ++mid;
-         }
+        mid = (low + hi) >> 1;
+        final int d = a[mid];
+        if (d == key)
+          return mid;
+        else if (d > key)
+          hi = mid - 1;
+        else
+          // This gets the insertion point right on the last loop.
+          low = ++mid;
       }
     return -mid - 1;
   }
 
   /**
-   * Perform a binary search of an int array for a key. The array must be
+   * Perform a binary search of a long array for a key. The array must be
    * sorted (as by the sort() method) - if it is not, the behaviour of this
    * method is undefined, and may be an infinite loop. If the array contains
    * the key more than once, any one of them may be found. Note: although the
@@ -223,38 +210,32 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
    */
-  public static int binarySearch(int[]a, int key)
+  public static int binarySearch(long[] a, long key)
   {
     int low = 0;
     int hi = a.length - 1;
     int mid = 0;
     while (low <= hi)
       {
-       mid = (low + hi) >> 1;
-       final int d = a[mid];
-       if (d == key)
-         {
-           return mid;
-         }
-       else if (d > key)
-         {
-           hi = mid - 1;
-         }
-       else
-         {
-           // This gets the insertion point right on the last loop
-           low = ++mid;
-         }
+        mid = (low + hi) >> 1;
+        final long d = a[mid];
+        if (d == key)
+          return mid;
+        else if (d > key)
+          hi = mid - 1;
+        else
+          // This gets the insertion point right on the last loop.
+          low = ++mid;
       }
     return -mid - 1;
   }
 
   /**
-   * Perform a binary search of a long array for a key. The array must be
+   * Perform a binary search of a float array for a key. The array must be
    * sorted (as by the sort() method) - if it is not, the behaviour of this
    * method is undefined, and may be an infinite loop. If the array contains
    * the key more than once, any one of them may be found. Note: although the
@@ -263,38 +244,33 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
    */
-  public static int binarySearch(long[]a, long key)
+  public static int binarySearch(float[] a, float key)
   {
+    // Must use Float.compare to take into account NaN, +-0.
     int low = 0;
     int hi = a.length - 1;
     int mid = 0;
     while (low <= hi)
       {
-       mid = (low + hi) >> 1;
-       final long d = a[mid];
-       if (d == key)
-         {
-           return mid;
-         }
-       else if (d > key)
-         {
-           hi = mid - 1;
-         }
-       else
-         {
-           // This gets the insertion point right on the last loop
-           low = ++mid;
-         }
+        mid = (low + hi) >> 1;
+        final int r = Float.compare(a[mid], key);
+        if (r == 0)
+          return mid;
+        else if (r > 0)
+          hi = mid - 1;
+        else
+          // This gets the insertion point right on the last loop
+          low = ++mid;
       }
     return -mid - 1;
   }
 
   /**
-   * Perform a binary search of a short array for a key. The array must be
+   * Perform a binary search of a double array for a key. The array must be
    * sorted (as by the sort() method) - if it is not, the behaviour of this
    * method is undefined, and may be an infinite loop. If the array contains
    * the key more than once, any one of them may be found. Note: although the
@@ -303,63 +279,27 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
-   */
-  public static int binarySearch(short[]a, short key)
-  {
-    int low = 0;
-    int hi = a.length - 1;
-    int mid = 0;
-    while (low <= hi)
-      {
-       mid = (low + hi) >> 1;
-       final short d = a[mid];
-       if (d == key)
-         {
-           return mid;
-         }
-       else if (d > key)
-         {
-           hi = mid - 1;
-         }
-       else
-         {
-           // This gets the insertion point right on the last loop
-           low = ++mid;
-         }
-      }
-    return -mid - 1;
-  }
-
-  /**
-   * This method does the work for the Object binary search methods. 
-   * @exception NullPointerException if the specified comparator is null.
-   * @exception ClassCastException if the objects are not comparable by c.
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
    */
-  private static int objectSearch(Object[]a, Object key, final Comparator c)
+  public static int binarySearch(double[] a, double key)
   {
+    // Must use Double.compare to take into account NaN, +-0.
     int low = 0;
     int hi = a.length - 1;
     int mid = 0;
     while (low <= hi)
       {
-       mid = (low + hi) >> 1;
-       final int d = c.compare(key, a[mid]);
-       if (d == 0)
-         {
-           return mid;
-         }
-       else if (d < 0)
-         {
-           hi = mid - 1;
-         }
-       else
-         {
-           // This gets the insertion point right on the last loop
-           low = ++mid;
-         }         
+        mid = (low + hi) >> 1;
+        final int r = Double.compare(a[mid], key);
+        if (r == 0)
+          return mid;
+        else if (r > 0)
+          hi = mid - 1;
+        else
+          // This gets the insertion point right on the last loop
+          low = ++mid;
       }
     return -mid - 1;
   }
@@ -376,16 +316,16 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
-   * @exception ClassCastException if key could not be compared with one of the
-   *   elements of a
-   * @exception NullPointerException if a null element has compareTo called
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
+   * @throws ClassCastException if key could not be compared with one of the
+   *         elements of a
+   * @throws NullPointerException if a null element in a is compared
    */
-  public static int binarySearch(Object[]a, Object key)
+  public static int binarySearch(Object[] a, Object key)
   {
-    return objectSearch(a, key, defaultComparator);
+    return binarySearch(a, key, null);
   }
 
   /**
@@ -400,343 +340,310 @@ public class Arrays
    *
    * @param a the array to search (must be sorted)
    * @param key the value to search for
-   * @param c the comparator by which the array is sorted
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
-   * @exception ClassCastException if key could not be compared with one of the
-   *   elements of a
+   * @param c the comparator by which the array is sorted; or null to
+   *        use the elements' natural order
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value.
+   * @throws ClassCastException if key could not be compared with one of the
+   *         elements of a
+   * @throws NullPointerException if a null element is compared with natural
+   *         ordering (only possible when c is null)
    */
-  public static int binarySearch(Object[]a, Object key, Comparator c)
+  public static int binarySearch(Object[] a, Object key, Comparator c)
   {
-    return objectSearch(a, key, c);
+    int low = 0;
+    int hi = a.length - 1;
+    int mid = 0;
+    while (low <= hi)
+      {
+        mid = (low + hi) >> 1;
+        final int d = Collections.compare(key, a[mid], c);
+        if (d == 0)
+          return mid;
+        else if (d < 0)
+          hi = mid - 1;
+        else
+          // This gets the insertion point right on the last loop
+          low = ++mid;
+      }
+    return -mid - 1;
   }
 
+\f
+// equals
   /**
-   * Compare two byte arrays for equality.
+   * Compare two boolean arrays for equality.
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a2 is of the same length
-   *   as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+   * @return true if a1 and a2 are both null, or if a2 is of the same length
+   *         as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
    */
-  public static boolean equals(byte[]a1, byte[]a2)
+  public static boolean equals(boolean[] a1, boolean[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (a1[i] != a2[i])
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (a1[i] != a2[i])
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
 
     return false;
   }
 
   /**
-   * Compare two char arrays for equality.
+   * Compare two byte arrays for equality.
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a2 is of the same length
-   *   as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+   * @return true if a1 and a2 are both null, or if a2 is of the same length
+   *         as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
    */
-  public static boolean equals(char[]a1, char[]a2)
+  public static boolean equals(byte[] a1, byte[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (a1[i] != a2[i])
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (a1[i] != a2[i])
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
-
     return false;
   }
 
   /**
-   * Compare two double arrays for equality.
+   * Compare two char arrays for equality.
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a2 is of the same length
-   *   as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+   * @return true if a1 and a2 are both null, or if a2 is of the same length
+   *         as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
    */
-  public static boolean equals(double[]a1, double[]a2)
+  public static boolean equals(char[] a1, char[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (a1[i] != a2[i])
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (a1[i] != a2[i])
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
-
     return false;
   }
 
   /**
-   * Compare two float arrays for equality.
+   * Compare two short arrays for equality.
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a2 is of the same length
-   *   as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+   * @return true if a1 and a2 are both null, or if a2 is of the same length
+   *         as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
    */
-  public static boolean equals(float[]a1, float[]a2)
+  public static boolean equals(short[] a1, short[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (a1[i] != a2[i])
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (a1[i] != a2[i])
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
-
     return false;
   }
 
   /**
-   * Compare two long arrays for equality.
+   * Compare two int arrays for equality.
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a2 is of the same length
-   *   as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+   * @return true if a1 and a2 are both null, or if a2 is of the same length
+   *         as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
    */
-  public static boolean equals(long[]a1, long[]a2)
+  public static boolean equals(int[] a1, int[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (a1[i] != a2[i])
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (a1[i] != a2[i])
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
-
     return false;
   }
 
   /**
-   * Compare two short arrays for equality.
+   * Compare two long arrays for equality.
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a2 is of the same length
-   *   as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+   * @return true if a1 and a2 are both null, or if a2 is of the same length
+   *         as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
    */
-  public static boolean equals(short[]a1, short[]a2)
+  public static boolean equals(long[] a1, long[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (a1[i] != a2[i])
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (a1[i] != a2[i])
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
-
     return false;
   }
 
   /**
-   * Compare two boolean arrays for equality.
+   * Compare two float arrays for equality.
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a2 is of the same length
-   *   as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+   * @return true if a1 and a2 are both null, or if a2 is of the same length
+   *         as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
    */
-  public static boolean equals(boolean[]a1, boolean[]a2)
+  public static boolean equals(float[] a1, float[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
+    // Must use Float.compare to take into account NaN, +-0.
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (a1[i] != a2[i])
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (Float.compare(a1[i], a2[i]) != 0)
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
-
     return false;
   }
 
   /**
-   * Compare two int arrays for equality.
+   * Compare two double arrays for equality.
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a2 is of the same length
-   *   as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+   * @return true if a1 and a2 are both null, or if a2 is of the same length
+   *         as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
    */
-  public static boolean equals(int[]a1, int[]a2)
+  public static boolean equals(double[] a1, double[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
+    // Must use Double.compare to take into account NaN, +-0.
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (a1[i] != a2[i])
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (Double.compare(a1[i], a2[i]) != 0)
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
-
     return false;
   }
 
@@ -745,54 +652,46 @@ public class Arrays
    *
    * @param a1 the first array to compare
    * @param a2 the second array to compare
-   * @returns true if a1 and a2 are both null, or if a1 is of the same length
-   *   as a2, and for each 0 <= i < a.length, a1[i] == null ? a2[i] == null :
-   *   a1[i].equals(a2[i]).
+   * @return true if a1 and a2 are both null, or if a1 is of the same length
+   *         as a2, and for each 0 <= i < a.length, a1[i] == null ?
+   *         a2[i] == null : a1[i].equals(a2[i]).
    */
-  public static boolean equals(Object[]a1, Object[]a2)
+  public static boolean equals(Object[] a1, Object[] a2)
   {
     // Quick test which saves comparing elements of the same array, and also
     // catches the case that both are null.
     if (a1 == a2)
-      {
-       return true;
-      }
-      
+      return true;
+
     try
       {
-       // If they're the same length, test each element
-       if (a1.length == a2.length)
-         {
-           for (int i = 0; i < a1.length; i++)
-             {
-               if (!(a1[i] == null ? a2[i] == null : a1[i].equals(a2[i])))
-                 {
-                   return false;
-                 }
-             }
-           return true;
-         }
-
-       // If a1 == null or a2 == null but not both then we will get a NullPointer
+        // If they're the same length, test each element
+        if (a1.length == a2.length)
+          {
+            int i = a1.length;
+            while (--i >= 0)
+              if (! AbstractCollection.equals(a1[i], a2[i]))
+                return false;
+            return true;
+          }
       }
     catch (NullPointerException e)
       {
+        // If one is null, we get a harmless NullPointerException
       }
-
     return false;
   }
 
+\f
+// fill
   /**
    * Fill an array with a boolean value.
    *
    * @param a the array to fill
    * @param val the value to fill it with
    */
-  public static void fill(boolean[]a, boolean val)
+  public static void fill(boolean[] a, boolean val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
@@ -803,13 +702,16 @@ public class Arrays
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(boolean[]a, int fromIndex, int toIndex, boolean val)
+  public static void fill(boolean[] a, int fromIndex, int toIndex, boolean val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
   /**
@@ -818,11 +720,8 @@ public class Arrays
    * @param a the array to fill
    * @param val the value to fill it with
    */
-  public static void fill(byte[]a, byte val)
+  public static void fill(byte[] a, byte val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
@@ -833,13 +732,16 @@ public class Arrays
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(byte[]a, int fromIndex, int toIndex, byte val)
+  public static void fill(byte[] a, int fromIndex, int toIndex, byte val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
   /**
@@ -848,11 +750,8 @@ public class Arrays
    * @param a the array to fill
    * @param val the value to fill it with
    */
-  public static void fill(char[]a, char val)
+  public static void fill(char[] a, char val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
@@ -863,163 +762,166 @@ public class Arrays
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(char[]a, int fromIndex, int toIndex, char val)
+  public static void fill(char[] a, int fromIndex, int toIndex, char val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
   /**
-   * Fill an array with a double value.
+   * Fill an array with a short value.
    *
    * @param a the array to fill
    * @param val the value to fill it with
    */
-  public static void fill(double[]a, double val)
+  public static void fill(short[] a, short val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
   /**
-   * Fill a range of an array with a double value.
+   * Fill a range of an array with a short value.
    *
    * @param a the array to fill
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(double[]a, int fromIndex, int toIndex, double val)
+  public static void fill(short[] a, int fromIndex, int toIndex, short val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
   /**
-   * Fill an array with a float value.
+   * Fill an array with an int value.
    *
    * @param a the array to fill
    * @param val the value to fill it with
    */
-  public static void fill(float[]a, float val)
+  public static void fill(int[] a, int val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
   /**
-   * Fill a range of an array with a float value.
+   * Fill a range of an array with an int value.
    *
    * @param a the array to fill
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(float[]a, int fromIndex, int toIndex, float val)
+  public static void fill(int[] a, int fromIndex, int toIndex, int val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
   /**
-   * Fill an array with an int value.
+   * Fill an array with a long value.
    *
    * @param a the array to fill
    * @param val the value to fill it with
    */
-  public static void fill(int[]a, int val)
+  public static void fill(long[] a, long val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
   /**
-   * Fill a range of an array with an int value.
+   * Fill a range of an array with a long value.
    *
    * @param a the array to fill
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(int[]a, int fromIndex, int toIndex, int val)
+  public static void fill(long[] a, int fromIndex, int toIndex, long val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
   /**
-   * Fill an array with a long value.
+   * Fill an array with a float value.
    *
    * @param a the array to fill
    * @param val the value to fill it with
    */
-  public static void fill(long[]a, long val)
+  public static void fill(float[] a, float val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
   /**
-   * Fill a range of an array with a long value.
+   * Fill a range of an array with a float value.
    *
    * @param a the array to fill
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(long[]a, int fromIndex, int toIndex, long val)
+  public static void fill(float[] a, int fromIndex, int toIndex, float val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
   /**
-   * Fill an array with a short value.
+   * Fill an array with a double value.
    *
    * @param a the array to fill
    * @param val the value to fill it with
    */
-  public static void fill(short[]a, short val)
+  public static void fill(double[] a, double val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
   /**
-   * Fill a range of an array with a short value.
+   * Fill a range of an array with a double value.
    *
    * @param a the array to fill
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(short[]a, int fromIndex, int toIndex, short val)
+  public static void fill(double[] a, int fromIndex, int toIndex, double val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
   /**
@@ -1027,14 +929,11 @@ public class Arrays
    *
    * @param a the array to fill
    * @param val the value to fill it with
-   * @exception ClassCastException if val is not an instance of the element
-   *   type of a.
+   * @throws ClassCastException if val is not an instance of the element
+   *         type of a.
    */
-  public static void fill(Object[]a, Object val)
+  public static void fill(Object[] a, Object val)
   {
-    // This implementation is slightly inefficient timewise, but the extra
-    // effort over inlining it is O(1) and small, and I refuse to repeat code
-    // if it can be helped.
     fill(a, 0, a.length, val);
   }
 
@@ -1045,926 +944,1191 @@ public class Arrays
    * @param fromIndex the index to fill from, inclusive
    * @param toIndex the index to fill to, exclusive
    * @param val the value to fill with
-   * @exception ClassCastException if val is not an instance of the element
-   *   type of a.
+   * @throws ClassCastException if val is not an instance of the element
+   *         type of a.
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
    */
-  public static void fill(Object[]a, int fromIndex, int toIndex, Object val)
+  public static void fill(Object[] a, int fromIndex, int toIndex, Object val)
   {
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
     for (int i = fromIndex; i < toIndex; i++)
-      {
-       a[i] = val;
-      }
+      a[i] = val;
   }
 
+\f
+// sort
   // Thanks to Paul Fisher <rao@gnu.org> for finding this quicksort algorithm
-  // as specified by Sun and porting it to Java.
+  // as specified by Sun and porting it to Java. The algorithm is an optimised
+  // quicksort, as described in Jon L. Bentley and M. Douglas McIlroy's
+  // "Engineering a Sort Function", Software-Practice and Experience, Vol.
+  // 23(11) P. 1249-1265 (November 1993). This algorithm gives n*log(n)
+  // performance on many arrays that would take quadratic time with a standard
+  // quicksort.
 
   /**
-   * Sort a byte array into ascending order. The sort algorithm is an optimised
-   * quicksort, as described in Jon L. Bentley and M. Douglas McIlroy's
-   * "Engineering a Sort Function", Software-Practice and Experience, Vol.
-   * 23(11) P. 1249-1265 (November 1993). This algorithm gives nlog(n)
-   * performance on many arrays that would take quadratic time with a standard
-   * quicksort.
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
    *
-   * @param a the array to sort
+   * @param a the byte array to sort
    */
-  public static void sort(byte[]a)
+  public static void sort(byte[] a)
   {
     qsort(a, 0, a.length);
   }
 
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the byte array to sort
+   * @param fromIndex the first index to sort (inclusive)
+   * @param toIndex the last index to sort (exclusive)
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
+   */
   public static void sort(byte[] a, int fromIndex, int toIndex)
   {
-    qsort(a, fromIndex, toIndex);
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
+    qsort(a, fromIndex, toIndex - fromIndex);
   }
 
-  private static int med3(int a, int b, int c, byte[]d)
+  /**
+   * Finds the index of the median of three array elements.
+   *
+   * @param a the first index
+   * @param b the second index
+   * @param c the third index
+   * @param d the array
+   * @return the index (a, b, or c) which has the middle value of the three
+   */
+  private static int med3(int a, int b, int c, byte[] d)
   {
-    return d[a] < d[b] ? 
-               (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
-            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a);
+    return (d[a] < d[b]
+            ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
   }
 
-  private static void swap(int i, int j, byte[]a)
+  /**
+   * Swaps the elements at two locations of an array
+   *
+   * @param i the first index
+   * @param j the second index
+   * @param a the array
+   */
+  private static void swap(int i, int j, byte[] a)
   {
     byte c = a[i];
     a[i] = a[j];
     a[j] = c;
   }
 
-  private static void qsort(byte[]a, int start, int n)
-  {
-    // use an insertion sort on small arrays
-    if (n <= 7)
-      {
-       for (int i = start + 1; i < start + n; i++)
-         for (int j = i; j > 0 && a[j - 1] > a[j]; j--)
-           swap(j, j - 1, a);
-       return;
-      }
-
-    int pm = n / 2;            // small arrays, middle element
-    if (n > 7)
-      {
-       int pl = start;
-       int pn = start + n - 1;
-
-       if (n > 40)
-         {                     // big arrays, pseudomedian of 9
-           int s = n / 8;
-           pl = med3(pl, pl + s, pl + 2 * s, a);
-           pm = med3(pm - s, pm, pm + s, a);
-           pn = med3(pn - 2 * s, pn - s, pn, a);
-         }
-       pm = med3(pl, pm, pn, a);       // mid-size, med of 3
-      }
-
-    int pa, pb, pc, pd, pv;
-    int r;
-
-    pv = start;
-    swap(pv, pm, a);
-    pa = pb = start;
-    pc = pd = start + n - 1;
-
-    for (;;)
-      {
-       while (pb <= pc && (r = a[pb] - a[pv]) <= 0)
-         {
-           if (r == 0)
-             {
-               swap(pa, pb, a);
-               pa++;
-             }
-           pb++;
-         }
-       while (pc >= pb && (r = a[pc] - a[pv]) >= 0)
-         {
-           if (r == 0)
-             {
-               swap(pc, pd, a);
-               pd--;
-             }
-           pc--;
-         }
-       if (pb > pc)
-         break;
-       swap(pb, pc, a);
-       pb++;
-       pc--;
-      }
-    int pn = start + n;
-    int s;
-    s = Math.min(pa - start, pb - pa);
-    vecswap(start, pb - s, s, a);
-    s = Math.min(pd - pc, pn - pd - 1);
-    vecswap(pb, pn - s, s, a);
-    if ((s = pb - pa) > 1)
-      qsort(a, start, s);
-    if ((s = pd - pc) > 1)
-      qsort(a, pn - s, s);
-  }
-
-  private static void vecswap(int i, int j, int n, byte[]a)
+  /**
+   * Swaps two ranges of an array.
+   *
+   * @param i the first range start
+   * @param j the second range start
+   * @param n the element count
+   * @param a the array
+   */
+  private static void vecswap(int i, int j, int n, byte[] a)
   {
-    for (; n > 0; i++, j++, n--)
+    for ( ; n > 0; i++, j++, n--)
       swap(i, j, a);
   }
 
   /**
-   * Sort a char array into ascending order. The sort algorithm is an optimised
-   * quicksort, as described in Jon L. Bentley and M. Douglas McIlroy's
-   * "Engineering a Sort Function", Software-Practice and Experience, Vol.
-   * 23(11) P. 1249-1265 (November 1993). This algorithm gives nlog(n)
-   * performance on many arrays that would take quadratic time with a standard
-   * quicksort.
+   * Performs a recursive modified quicksort.
    *
    * @param a the array to sort
+   * @param from the start index (inclusive)
+   * @param count the number of elements to sort
+   */
+  private static void qsort(byte[] array, int from, int count)
+  {
+    // Use an insertion sort on small arrays.
+    if (count <= 7)
+      {
+        for (int i = from + 1; i < from + count; i++)
+          for (int j = i; j > 0 && array[j - 1] > array[j]; j--)
+            swap(j, j - 1, array);
+        return;
+      }
+
+    // Determine a good median element.
+    int mid = count / 2;
+    int lo = from;
+    int hi = from + count - 1;
+
+    if (count > 40)
+      { // big arrays, pseudomedian of 9
+        int s = count / 8;
+        lo = med3(lo, lo + s, lo + s + s, array);
+        mid = med3(mid - s, mid, mid + s, array);
+        hi = med3(hi - s - s, hi - s, hi, array);
+      }
+    mid = med3(lo, mid, hi, array);
+
+    int a, b, c, d;
+    int comp;
+
+    // Pull the median element out of the fray, and use it as a pivot.
+    swap(from, mid, array);
+    a = b = from + 1;
+    c = d = hi;
+
+    // Repeatedly move b and c to each other, swapping elements so
+    // that all elements before index b are less than the pivot, and all
+    // elements after index c are greater than the pivot. a and b track
+    // the elements equal to the pivot.
+    while (true)
+      {
+        while (b <= c && (comp = array[b] - array[from]) <= 0)
+          {
+            if (comp == 0)
+              {
+                swap(a, b, array);
+                a++;
+              }
+            b++;
+          }
+        while (c >= b && (comp = array[c] - array[from]) >= 0)
+          {
+            if (comp == 0)
+              {
+                swap(c, d, array);
+                d--;
+              }
+            c--;
+          }
+        if (b > c)
+          break;
+        swap(b, c, array);
+        b++;
+        c--;
+      }
+
+    // Swap pivot(s) back in place, the recurse on left and right sections.
+    int span;
+    span = Math.min(a - from, b - a);
+    vecswap(from, b - span, span, array);
+
+    span = Math.min(d - c, hi - d - 1);
+    vecswap(b, hi - span + 1, span, array);
+
+    span = b - a;
+    if (span > 1)
+      qsort(array, from, span);
+
+    span = d - c;
+    if (span > 1)
+      qsort(array, hi - span + 1, span);
+  }
+
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the char array to sort
    */
-  public static void sort(char[]a)
+  public static void sort(char[] a)
   {
     qsort(a, 0, a.length);
   }
 
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the char array to sort
+   * @param fromIndex the first index to sort (inclusive)
+   * @param toIndex the last index to sort (exclusive)
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
+   */
   public static void sort(char[] a, int fromIndex, int toIndex)
   {
-    qsort(a, fromIndex, toIndex);
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
+    qsort(a, fromIndex, toIndex - fromIndex);
   }
 
-  private static int med3(int a, int b, int c, char[]d)
+  /**
+   * Finds the index of the median of three array elements.
+   *
+   * @param a the first index
+   * @param b the second index
+   * @param c the third index
+   * @param d the array
+   * @return the index (a, b, or c) which has the middle value of the three
+   */
+  private static int med3(int a, int b, int c, char[] d)
   {
-    return d[a] < d[b] ? 
-               (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
-            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a);
+    return (d[a] < d[b]
+            ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
   }
 
-  private static void swap(int i, int j, char[]a)
+  /**
+   * Swaps the elements at two locations of an array
+   *
+   * @param i the first index
+   * @param j the second index
+   * @param a the array
+   */
+  private static void swap(int i, int j, char[] a)
   {
     char c = a[i];
     a[i] = a[j];
     a[j] = c;
   }
 
-  private static void qsort(char[]a, int start, int n)
-  {
-    // use an insertion sort on small arrays
-    if (n <= 7)
-      {
-       for (int i = start + 1; i < start + n; i++)
-         for (int j = i; j > 0 && a[j - 1] > a[j]; j--)
-           swap(j, j - 1, a);
-       return;
-      }
-
-    int pm = n / 2;            // small arrays, middle element
-    if (n > 7)
-      {
-       int pl = start;
-       int pn = start + n - 1;
-
-       if (n > 40)
-         {                     // big arrays, pseudomedian of 9
-           int s = n / 8;
-           pl = med3(pl, pl + s, pl + 2 * s, a);
-           pm = med3(pm - s, pm, pm + s, a);
-           pn = med3(pn - 2 * s, pn - s, pn, a);
-         }
-       pm = med3(pl, pm, pn, a);       // mid-size, med of 3
-      }
-
-    int pa, pb, pc, pd, pv;
-    int r;
-
-    pv = start;
-    swap(pv, pm, a);
-    pa = pb = start;
-    pc = pd = start + n - 1;
-
-    for (;;)
-      {
-       while (pb <= pc && (r = a[pb] - a[pv]) <= 0)
-         {
-           if (r == 0)
-             {
-               swap(pa, pb, a);
-               pa++;
-             }
-           pb++;
-         }
-       while (pc >= pb && (r = a[pc] - a[pv]) >= 0)
-         {
-           if (r == 0)
-             {
-               swap(pc, pd, a);
-               pd--;
-             }
-           pc--;
-         }
-       if (pb > pc)
-         break;
-       swap(pb, pc, a);
-       pb++;
-       pc--;
-      }
-    int pn = start + n;
-    int s;
-    s = Math.min(pa - start, pb - pa);
-    vecswap(start, pb - s, s, a);
-    s = Math.min(pd - pc, pn - pd - 1);
-    vecswap(pb, pn - s, s, a);
-    if ((s = pb - pa) > 1)
-      qsort(a, start, s);
-    if ((s = pd - pc) > 1)
-      qsort(a, pn - s, s);
-  }
-
-  private static void vecswap(int i, int j, int n, char[]a)
+  /**
+   * Swaps two ranges of an array.
+   *
+   * @param i the first range start
+   * @param j the second range start
+   * @param n the element count
+   * @param a the array
+   */
+  private static void vecswap(int i, int j, int n, char[] a)
   {
-    for (; n > 0; i++, j++, n--)
+    for ( ; n > 0; i++, j++, n--)
       swap(i, j, a);
   }
 
   /**
-   * Sort a double array into ascending order. The sort algorithm is an
-   * optimised quicksort, as described in Jon L. Bentley and M. Douglas
-   * McIlroy's "Engineering a Sort Function", Software-Practice and Experience,
-   * Vol. 23(11) P. 1249-1265 (November 1993). This algorithm gives nlog(n)
-   * performance on many arrays that would take quadratic time with a standard
-   * quicksort. Note that this implementation, like Sun's, has undefined
-   * behaviour if the array contains any NaN values.
+   * Performs a recursive modified quicksort.
    *
    * @param a the array to sort
+   * @param from the start index (inclusive)
+   * @param count the number of elements to sort
+   */
+  private static void qsort(char[] array, int from, int count)
+  {
+    // Use an insertion sort on small arrays.
+    if (count <= 7)
+      {
+        for (int i = from + 1; i < from + count; i++)
+          for (int j = i; j > 0 && array[j - 1] > array[j]; j--)
+            swap(j, j - 1, array);
+        return;
+      }
+
+    // Determine a good median element.
+    int mid = count / 2;
+    int lo = from;
+    int hi = from + count - 1;
+
+    if (count > 40)
+      { // big arrays, pseudomedian of 9
+        int s = count / 8;
+        lo = med3(lo, lo + s, lo + s + s, array);
+        mid = med3(mid - s, mid, mid + s, array);
+        hi = med3(hi - s - s, hi - s, hi, array);
+      }
+    mid = med3(lo, mid, hi, array);
+
+    int a, b, c, d;
+    int comp;
+
+    // Pull the median element out of the fray, and use it as a pivot.
+    swap(from, mid, array);
+    a = b = from + 1;
+    c = d = hi;
+
+    // Repeatedly move b and c to each other, swapping elements so
+    // that all elements before index b are less than the pivot, and all
+    // elements after index c are greater than the pivot. a and b track
+    // the elements equal to the pivot.
+    while (true)
+      {
+        while (b <= c && (comp = array[b] - array[from]) <= 0)
+          {
+            if (comp == 0)
+              {
+                swap(a, b, array);
+                a++;
+              }
+            b++;
+          }
+        while (c >= b && (comp = array[c] - array[from]) >= 0)
+          {
+            if (comp == 0)
+              {
+                swap(c, d, array);
+                d--;
+              }
+            c--;
+          }
+        if (b > c)
+          break;
+        swap(b, c, array);
+        b++;
+        c--;
+      }
+
+    // Swap pivot(s) back in place, the recurse on left and right sections.
+    int span;
+    span = Math.min(a - from, b - a);
+    vecswap(from, b - span, span, array);
+
+    span = Math.min(d - c, hi - d - 1);
+    vecswap(b, hi - span + 1, span, array);
+
+    span = b - a;
+    if (span > 1)
+      qsort(array, from, span);
+
+    span = d - c;
+    if (span > 1)
+      qsort(array, hi - span + 1, span);
+  }
+
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the short array to sort
    */
-  public static void sort(double[]a)
+  public static void sort(short[] a)
   {
     qsort(a, 0, a.length);
   }
 
-  public static void sort(double[] a, int fromIndex, int toIndex)
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the short array to sort
+   * @param fromIndex the first index to sort (inclusive)
+   * @param toIndex the last index to sort (exclusive)
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
+   */
+  public static void sort(short[] a, int fromIndex, int toIndex)
   {
-    qsort(a, fromIndex, toIndex);
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
+    qsort(a, fromIndex, toIndex - fromIndex);
   }
 
-  private static int med3(int a, int b, int c, double[]d)
+  /**
+   * Finds the index of the median of three array elements.
+   *
+   * @param a the first index
+   * @param b the second index
+   * @param c the third index
+   * @param d the array
+   * @return the index (a, b, or c) which has the middle value of the three
+   */
+  private static int med3(int a, int b, int c, short[] d)
   {
-    return d[a] < d[b] ? 
-               (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
-            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a);
+    return (d[a] < d[b]
+            ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
   }
 
-  private static void swap(int i, int j, double[]a)
+  /**
+   * Swaps the elements at two locations of an array
+   *
+   * @param i the first index
+   * @param j the second index
+   * @param a the array
+   */
+  private static void swap(int i, int j, short[] a)
   {
-    double c = a[i];
+    short c = a[i];
     a[i] = a[j];
     a[j] = c;
   }
 
-  private static void qsort(double[]a, int start, int n)
-  {
-    // use an insertion sort on small arrays
-    if (n <= 7)
-      {
-       for (int i = start + 1; i < start + n; i++)
-         for (int j = i; j > 0 && a[j - 1] > a[j]; j--)
-           swap(j, j - 1, a);
-       return;
-      }
-
-    int pm = n / 2;            // small arrays, middle element
-    if (n > 7)
-      {
-       int pl = start;
-       int pn = start + n - 1;
-
-       if (n > 40)
-         {                     // big arrays, pseudomedian of 9
-           int s = n / 8;
-           pl = med3(pl, pl + s, pl + 2 * s, a);
-           pm = med3(pm - s, pm, pm + s, a);
-           pn = med3(pn - 2 * s, pn - s, pn, a);
-         }
-       pm = med3(pl, pm, pn, a);       // mid-size, med of 3
-      }
-
-    int pa, pb, pc, pd, pv;
-    double r;
-
-    pv = start;
-    swap(pv, pm, a);
-    pa = pb = start;
-    pc = pd = start + n - 1;
-
-    for (;;)
-      {
-       while (pb <= pc && (r = a[pb] - a[pv]) <= 0)
-         {
-           if (r == 0)
-             {
-               swap(pa, pb, a);
-               pa++;
-             }
-           pb++;
-         }
-       while (pc >= pb && (r = a[pc] - a[pv]) >= 0)
-         {
-           if (r == 0)
-             {
-               swap(pc, pd, a);
-               pd--;
-             }
-           pc--;
-         }
-       if (pb > pc)
-         break;
-       swap(pb, pc, a);
-       pb++;
-       pc--;
-      }
-    int pn = start + n;
-    int s;
-    s = Math.min(pa - start, pb - pa);
-    vecswap(start, pb - s, s, a);
-    s = Math.min(pd - pc, pn - pd - 1);
-    vecswap(pb, pn - s, s, a);
-    if ((s = pb - pa) > 1)
-      qsort(a, start, s);
-    if ((s = pd - pc) > 1)
-      qsort(a, pn - s, s);
-  }
-
-  private static void vecswap(int i, int j, int n, double[]a)
+  /**
+   * Swaps two ranges of an array.
+   *
+   * @param i the first range start
+   * @param j the second range start
+   * @param n the element count
+   * @param a the array
+   */
+  private static void vecswap(int i, int j, int n, short[] a)
   {
-    for (; n > 0; i++, j++, n--)
+    for ( ; n > 0; i++, j++, n--)
       swap(i, j, a);
   }
 
   /**
-   * Sort a float array into ascending order. The sort algorithm is an
-   * optimised quicksort, as described in Jon L. Bentley and M. Douglas
-   * McIlroy's "Engineering a Sort Function", Software-Practice and Experience,
-   * Vol. 23(11) P. 1249-1265 (November 1993). This algorithm gives nlog(n)
-   * performance on many arrays that would take quadratic time with a standard
-   * quicksort. Note that this implementation, like Sun's, has undefined
-   * behaviour if the array contains any NaN values.
+   * Performs a recursive modified quicksort.
    *
    * @param a the array to sort
+   * @param from the start index (inclusive)
+   * @param count the number of elements to sort
    */
-  public static void sort(float[]a)
+  private static void qsort(short[] array, int from, int count)
+  {
+    // Use an insertion sort on small arrays.
+    if (count <= 7)
+      {
+        for (int i = from + 1; i < from + count; i++)
+          for (int j = i; j > 0 && array[j - 1] > array[j]; j--)
+            swap(j, j - 1, array);
+        return;
+      }
+
+    // Determine a good median element.
+    int mid = count / 2;
+    int lo = from;
+    int hi = from + count - 1;
+
+    if (count > 40)
+      { // big arrays, pseudomedian of 9
+        int s = count / 8;
+        lo = med3(lo, lo + s, lo + s + s, array);
+        mid = med3(mid - s, mid, mid + s, array);
+        hi = med3(hi - s - s, hi - s, hi, array);
+      }
+    mid = med3(lo, mid, hi, array);
+
+    int a, b, c, d;
+    int comp;
+
+    // Pull the median element out of the fray, and use it as a pivot.
+    swap(from, mid, array);
+    a = b = from + 1;
+    c = d = hi;
+
+    // Repeatedly move b and c to each other, swapping elements so
+    // that all elements before index b are less than the pivot, and all
+    // elements after index c are greater than the pivot. a and b track
+    // the elements equal to the pivot.
+    while (true)
+      {
+        while (b <= c && (comp = array[b] - array[from]) <= 0)
+          {
+            if (comp == 0)
+              {
+                swap(a, b, array);
+                a++;
+              }
+            b++;
+          }
+        while (c >= b && (comp = array[c] - array[from]) >= 0)
+          {
+            if (comp == 0)
+              {
+                swap(c, d, array);
+                d--;
+              }
+            c--;
+          }
+        if (b > c)
+          break;
+        swap(b, c, array);
+        b++;
+        c--;
+      }
+
+    // Swap pivot(s) back in place, the recurse on left and right sections.
+    int span;
+    span = Math.min(a - from, b - a);
+    vecswap(from, b - span, span, array);
+
+    span = Math.min(d - c, hi - d - 1);
+    vecswap(b, hi - span + 1, span, array);
+
+    span = b - a;
+    if (span > 1)
+      qsort(array, from, span);
+
+    span = d - c;
+    if (span > 1)
+      qsort(array, hi - span + 1, span);
+  }
+
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the int array to sort
+   */
+  public static void sort(int[] a)
   {
     qsort(a, 0, a.length);
   }
 
-  public static void sort(float[] a, int fromIndex, int toIndex)
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the int array to sort
+   * @param fromIndex the first index to sort (inclusive)
+   * @param toIndex the last index to sort (exclusive)
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
+   */
+  public static void sort(int[] a, int fromIndex, int toIndex)
   {
-    qsort(a, fromIndex, toIndex);
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
+    qsort(a, fromIndex, toIndex - fromIndex);
   }
 
-  private static int med3(int a, int b, int c, float[]d)
+  /**
+   * Finds the index of the median of three array elements.
+   *
+   * @param a the first index
+   * @param b the second index
+   * @param c the third index
+   * @param d the array
+   * @return the index (a, b, or c) which has the middle value of the three
+   */
+  private static int med3(int a, int b, int c, int[] d)
   {
-    return d[a] < d[b] ? 
-               (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
-            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a);
+    return (d[a] < d[b]
+            ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
   }
 
-  private static void swap(int i, int j, float[]a)
+  /**
+   * Swaps the elements at two locations of an array
+   *
+   * @param i the first index
+   * @param j the second index
+   * @param a the array
+   */
+  private static void swap(int i, int j, int[] a)
   {
-    float c = a[i];
+    int c = a[i];
     a[i] = a[j];
     a[j] = c;
   }
 
-  private static void qsort(float[]a, int start, int n)
+  /**
+   * Swaps two ranges of an array.
+   *
+   * @param i the first range start
+   * @param j the second range start
+   * @param n the element count
+   * @param a the array
+   */
+  private static void vecswap(int i, int j, int n, int[] a)
   {
-    // use an insertion sort on small arrays
-    if (n <= 7)
-      {
-       for (int i = start + 1; i < start + n; i++)
-         for (int j = i; j > 0 && a[j - 1] > a[j]; j--)
-           swap(j, j - 1, a);
-       return;
-      }
-
-    int pm = n / 2;            // small arrays, middle element
-    if (n > 7)
-      {
-       int pl = start;
-       int pn = start + n - 1;
-
-       if (n > 40)
-         {                     // big arrays, pseudomedian of 9
-           int s = n / 8;
-           pl = med3(pl, pl + s, pl + 2 * s, a);
-           pm = med3(pm - s, pm, pm + s, a);
-           pn = med3(pn - 2 * s, pn - s, pn, a);
-         }
-       pm = med3(pl, pm, pn, a);       // mid-size, med of 3
-      }
-
-    int pa, pb, pc, pd, pv;
-    float r;
-
-    pv = start;
-    swap(pv, pm, a);
-    pa = pb = start;
-    pc = pd = start + n - 1;
-
-    for (;;)
-      {
-       while (pb <= pc && (r = a[pb] - a[pv]) <= 0)
-         {
-           if (r == 0)
-             {
-               swap(pa, pb, a);
-               pa++;
-             }
-           pb++;
-         }
-       while (pc >= pb && (r = a[pc] - a[pv]) >= 0)
-         {
-           if (r == 0)
-             {
-               swap(pc, pd, a);
-               pd--;
-             }
-           pc--;
-         }
-       if (pb > pc)
-         break;
-       swap(pb, pc, a);
-       pb++;
-       pc--;
-      }
-    int pn = start + n;
-    int s;
-    s = Math.min(pa - start, pb - pa);
-    vecswap(start, pb - s, s, a);
-    s = Math.min(pd - pc, pn - pd - 1);
-    vecswap(pb, pn - s, s, a);
-    if ((s = pb - pa) > 1)
-      qsort(a, start, s);
-    if ((s = pd - pc) > 1)
-      qsort(a, pn - s, s);
+    for ( ; n > 0; i++, j++, n--)
+      swap(i, j, a);
   }
 
-  private static void vecswap(int i, int j, int n, float[]a)
+  /**
+   * Compares two integers in natural order, since a - b is inadequate.
+   *
+   * @param a the first int
+   * @param b the second int
+   * @return &lt; 0, 0, or &gt; 0 accorting to the comparison
+   */
+  private static int compare(int a, int b)
   {
-    for (; n > 0; i++, j++, n--)
-      swap(i, j, a);
+    return a < b ? -1 : a == b ? 0 : 1;
   }
 
   /**
-   * Sort an int array into ascending order. The sort algorithm is an optimised
-   * quicksort, as described in Jon L. Bentley and M. Douglas McIlroy's
-   * "Engineering a Sort Function", Software-Practice and Experience, Vol.
-   * 23(11) P. 1249-1265 (November 1993). This algorithm gives nlog(n)
-   * performance on many arrays that would take quadratic time with a standard
-   * quicksort.
+   * Performs a recursive modified quicksort.
    *
    * @param a the array to sort
+   * @param from the start index (inclusive)
+   * @param count the number of elements to sort
    */
-  public static void sort(int[]a)
+  private static void qsort(int[] array, int from, int count)
+  {
+    // Use an insertion sort on small arrays.
+    if (count <= 7)
+      {
+        for (int i = from + 1; i < from + count; i++)
+          for (int j = i; j > 0 && array[j - 1] > array[j]; j--)
+            swap(j, j - 1, array);
+        return;
+      }
+
+    // Determine a good median element.
+    int mid = count / 2;
+    int lo = from;
+    int hi = from + count - 1;
+
+    if (count > 40)
+      { // big arrays, pseudomedian of 9
+        int s = count / 8;
+        lo = med3(lo, lo + s, lo + s + s, array);
+        mid = med3(mid - s, mid, mid + s, array);
+        hi = med3(hi - s - s, hi - s, hi, array);
+      }
+    mid = med3(lo, mid, hi, array);
+
+    int a, b, c, d;
+    int comp;
+
+    // Pull the median element out of the fray, and use it as a pivot.
+    swap(from, mid, array);
+    a = b = from + 1;
+    c = d = hi;
+
+    // Repeatedly move b and c to each other, swapping elements so
+    // that all elements before index b are less than the pivot, and all
+    // elements after index c are greater than the pivot. a and b track
+    // the elements equal to the pivot.
+    while (true)
+      {
+        while (b <= c && (comp = compare(array[b], array[from])) <= 0)
+          {
+            if (comp == 0)
+              {
+                swap(a, b, array);
+                a++;
+              }
+            b++;
+          }
+        while (c >= b && (comp = compare(array[c], array[from])) >= 0)
+          {
+            if (comp == 0)
+              {
+                swap(c, d, array);
+                d--;
+              }
+            c--;
+          }
+        if (b > c)
+          break;
+        swap(b, c, array);
+        b++;
+        c--;
+      }
+
+    // Swap pivot(s) back in place, the recurse on left and right sections.
+    int span;
+    span = Math.min(a - from, b - a);
+    vecswap(from, b - span, span, array);
+
+    span = Math.min(d - c, hi - d - 1);
+    vecswap(b, hi - span + 1, span, array);
+
+    span = b - a;
+    if (span > 1)
+      qsort(array, from, span);
+
+    span = d - c;
+    if (span > 1)
+      qsort(array, hi - span + 1, span);
+  }
+
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the long array to sort
+   */
+  public static void sort(long[] a)
   {
     qsort(a, 0, a.length);
   }
 
-  public static void sort(int[] a, int fromIndex, int toIndex)
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the long array to sort
+   * @param fromIndex the first index to sort (inclusive)
+   * @param toIndex the last index to sort (exclusive)
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
+   */
+  public static void sort(long[] a, int fromIndex, int toIndex)
   {
-    qsort(a, fromIndex, toIndex);
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
+    qsort(a, fromIndex, toIndex - fromIndex);
   }
 
-  private static int med3(int a, int b, int c, int[]d)
+  /**
+   * Finds the index of the median of three array elements.
+   *
+   * @param a the first index
+   * @param b the second index
+   * @param c the third index
+   * @param d the array
+   * @return the index (a, b, or c) which has the middle value of the three
+   */
+  private static int med3(int a, int b, int c, long[] d)
   {
-    return d[a] < d[b] ? 
-               (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
-            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a);
+    return (d[a] < d[b]
+            ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
   }
 
-  private static void swap(int i, int j, int[]a)
+  /**
+   * Swaps the elements at two locations of an array
+   *
+   * @param i the first index
+   * @param j the second index
+   * @param a the array
+   */
+  private static void swap(int i, int j, long[] a)
   {
-    int c = a[i];
+    long c = a[i];
     a[i] = a[j];
     a[j] = c;
   }
 
-  private static void qsort(int[]a, int start, int n)
+  /**
+   * Swaps two ranges of an array.
+   *
+   * @param i the first range start
+   * @param j the second range start
+   * @param n the element count
+   * @param a the array
+   */
+  private static void vecswap(int i, int j, int n, long[] a)
   {
-    // use an insertion sort on small arrays
-    if (n <= 7)
-      {
-       for (int i = start + 1; i < start + n; i++)
-         for (int j = i; j > 0 && a[j - 1] > a[j]; j--)
-           swap(j, j - 1, a);
-       return;
-      }
-
-    int pm = n / 2;            // small arrays, middle element
-    if (n > 7)
-      {
-       int pl = start;
-       int pn = start + n - 1;
-
-       if (n > 40)
-         {                     // big arrays, pseudomedian of 9
-           int s = n / 8;
-           pl = med3(pl, pl + s, pl + 2 * s, a);
-           pm = med3(pm - s, pm, pm + s, a);
-           pn = med3(pn - 2 * s, pn - s, pn, a);
-         }
-       pm = med3(pl, pm, pn, a);       // mid-size, med of 3
-      }
-
-    int pa, pb, pc, pd, pv;
-    int r;
-
-    pv = start;
-    swap(pv, pm, a);
-    pa = pb = start;
-    pc = pd = start + n - 1;
-
-    for (;;)
-      {
-       while (pb <= pc && (r = a[pb] - a[pv]) <= 0)
-         {
-           if (r == 0)
-             {
-               swap(pa, pb, a);
-               pa++;
-             }
-           pb++;
-         }
-       while (pc >= pb && (r = a[pc] - a[pv]) >= 0)
-         {
-           if (r == 0)
-             {
-               swap(pc, pd, a);
-               pd--;
-             }
-           pc--;
-         }
-       if (pb > pc)
-         break;
-       swap(pb, pc, a);
-       pb++;
-       pc--;
-      }
-    int pn = start + n;
-    int s;
-    s = Math.min(pa - start, pb - pa);
-    vecswap(start, pb - s, s, a);
-    s = Math.min(pd - pc, pn - pd - 1);
-    vecswap(pb, pn - s, s, a);
-    if ((s = pb - pa) > 1)
-      qsort(a, start, s);
-    if ((s = pd - pc) > 1)
-      qsort(a, pn - s, s);
+    for ( ; n > 0; i++, j++, n--)
+      swap(i, j, a);
   }
 
-  private static void vecswap(int i, int j, int n, int[]a)
+  /**
+   * Compares two longs in natural order, since a - b is inadequate.
+   *
+   * @param a the first long
+   * @param b the second long
+   * @return &lt; 0, 0, or &gt; 0 accorting to the comparison
+   */
+  private static int compare(long a, long b)
   {
-    for (; n > 0; i++, j++, n--)
-      swap(i, j, a);
+    return a < b ? -1 : a == b ? 0 : 1;
   }
 
   /**
-   * Sort a long array into ascending order. The sort algorithm is an optimised
-   * quicksort, as described in Jon L. Bentley and M. Douglas McIlroy's
-   * "Engineering a Sort Function", Software-Practice and Experience, Vol.
-   * 23(11) P. 1249-1265 (November 1993). This algorithm gives nlog(n)
-   * performance on many arrays that would take quadratic time with a standard
-   * quicksort.
+   * Performs a recursive modified quicksort.
    *
    * @param a the array to sort
+   * @param from the start index (inclusive)
+   * @param count the number of elements to sort
+   */
+  private static void qsort(long[] array, int from, int count)
+  {
+    // Use an insertion sort on small arrays.
+    if (count <= 7)
+      {
+        for (int i = from + 1; i < from + count; i++)
+          for (int j = i; j > 0 && array[j - 1] > array[j]; j--)
+            swap(j, j - 1, array);
+        return;
+      }
+
+    // Determine a good median element.
+    int mid = count / 2;
+    int lo = from;
+    int hi = from + count - 1;
+
+    if (count > 40)
+      { // big arrays, pseudomedian of 9
+        int s = count / 8;
+        lo = med3(lo, lo + s, lo + s + s, array);
+        mid = med3(mid - s, mid, mid + s, array);
+        hi = med3(hi - s - s, hi - s, hi, array);
+      }
+    mid = med3(lo, mid, hi, array);
+
+    int a, b, c, d;
+    int comp;
+
+    // Pull the median element out of the fray, and use it as a pivot.
+    swap(from, mid, array);
+    a = b = from + 1;
+    c = d = hi;
+
+    // Repeatedly move b and c to each other, swapping elements so
+    // that all elements before index b are less than the pivot, and all
+    // elements after index c are greater than the pivot. a and b track
+    // the elements equal to the pivot.
+    while (true)
+      {
+        while (b <= c && (comp = compare(array[b], array[from])) <= 0)
+          {
+            if (comp == 0)
+              {
+                swap(a, b, array);
+                a++;
+              }
+            b++;
+          }
+        while (c >= b && (comp = compare(array[c], array[from])) >= 0)
+          {
+            if (comp == 0)
+              {
+                swap(c, d, array);
+                d--;
+              }
+            c--;
+          }
+        if (b > c)
+          break;
+        swap(b, c, array);
+        b++;
+        c--;
+      }
+
+    // Swap pivot(s) back in place, the recurse on left and right sections.
+    int span;
+    span = Math.min(a - from, b - a);
+    vecswap(from, b - span, span, array);
+
+    span = Math.min(d - c, hi - d - 1);
+    vecswap(b, hi - span + 1, span, array);
+
+    span = b - a;
+    if (span > 1)
+      qsort(array, from, span);
+
+    span = d - c;
+    if (span > 1)
+      qsort(array, hi - span + 1, span);
+  }
+
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the float array to sort
    */
-  public static void sort(long[]a)
+  public static void sort(float[] a)
   {
     qsort(a, 0, a.length);
   }
 
-  public static void sort(long[] a, int fromIndex, int toIndex)
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the float array to sort
+   * @param fromIndex the first index to sort (inclusive)
+   * @param toIndex the last index to sort (exclusive)
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
+   */
+  public static void sort(float[] a, int fromIndex, int toIndex)
   {
-    qsort(a, fromIndex, toIndex);
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
+    qsort(a, fromIndex, toIndex - fromIndex);
   }
 
-  private static int med3(int a, int b, int c, long[]d)
+  /**
+   * Finds the index of the median of three array elements.
+   *
+   * @param a the first index
+   * @param b the second index
+   * @param c the third index
+   * @param d the array
+   * @return the index (a, b, or c) which has the middle value of the three
+   */
+  private static int med3(int a, int b, int c, float[] d)
   {
-    return d[a] < d[b] ? 
-               (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
-            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a);
+    return (Float.compare(d[a], d[b]) < 0
+            ? (Float.compare(d[b], d[c]) < 0 ? b
+               : Float.compare(d[a], d[c]) < 0 ? c : a)
+            : (Float.compare(d[b], d[c]) > 0 ? b
+               : Float.compare(d[a], d[c]) > 0 ? c : a));
   }
 
-  private static void swap(int i, int j, long[]a)
+  /**
+   * Swaps the elements at two locations of an array
+   *
+   * @param i the first index
+   * @param j the second index
+   * @param a the array
+   */
+  private static void swap(int i, int j, float[] a)
   {
-    long c = a[i];
+    float c = a[i];
     a[i] = a[j];
     a[j] = c;
   }
 
-  private static void qsort(long[]a, int start, int n)
-  {
-    // use an insertion sort on small arrays
-    if (n <= 7)
-      {
-       for (int i = start + 1; i < start + n; i++)
-         for (int j = i; j > 0 && a[j - 1] > a[j]; j--)
-           swap(j, j - 1, a);
-       return;
-      }
-
-    int pm = n / 2;            // small arrays, middle element
-    if (n > 7)
-      {
-       int pl = start;
-       int pn = start + n - 1;
-
-       if (n > 40)
-         {                     // big arrays, pseudomedian of 9
-           int s = n / 8;
-           pl = med3(pl, pl + s, pl + 2 * s, a);
-           pm = med3(pm - s, pm, pm + s, a);
-           pn = med3(pn - 2 * s, pn - s, pn, a);
-         }
-       pm = med3(pl, pm, pn, a);       // mid-size, med of 3
-      }
-
-    int pa, pb, pc, pd, pv;
-    long r;
-
-    pv = start;
-    swap(pv, pm, a);
-    pa = pb = start;
-    pc = pd = start + n - 1;
-
-    for (;;)
-      {
-       while (pb <= pc && (r = a[pb] - a[pv]) <= 0)
-         {
-           if (r == 0)
-             {
-               swap(pa, pb, a);
-               pa++;
-             }
-           pb++;
-         }
-       while (pc >= pb && (r = a[pc] - a[pv]) >= 0)
-         {
-           if (r == 0)
-             {
-               swap(pc, pd, a);
-               pd--;
-             }
-           pc--;
-         }
-       if (pb > pc)
-         break;
-       swap(pb, pc, a);
-       pb++;
-       pc--;
-      }
-    int pn = start + n;
-    int s;
-    s = Math.min(pa - start, pb - pa);
-    vecswap(start, pb - s, s, a);
-    s = Math.min(pd - pc, pn - pd - 1);
-    vecswap(pb, pn - s, s, a);
-    if ((s = pb - pa) > 1)
-      qsort(a, start, s);
-    if ((s = pd - pc) > 1)
-      qsort(a, pn - s, s);
-  }
-
-  private static void vecswap(int i, int j, int n, long[]a)
+  /**
+   * Swaps two ranges of an array.
+   *
+   * @param i the first range start
+   * @param j the second range start
+   * @param n the element count
+   * @param a the array
+   */
+  private static void vecswap(int i, int j, int n, float[] a)
   {
-    for (; n > 0; i++, j++, n--)
+    for ( ; n > 0; i++, j++, n--)
       swap(i, j, a);
   }
 
   /**
-   * Sort a short array into ascending order. The sort algorithm is an
-   * optimised quicksort, as described in Jon L. Bentley and M. Douglas
-   * McIlroy's "Engineering a Sort Function", Software-Practice and Experience,
-   * Vol. 23(11) P. 1249-1265 (November 1993). This algorithm gives nlog(n)
-   * performance on many arrays that would take quadratic time with a standard
-   * quicksort.
+   * Performs a recursive modified quicksort.
    *
    * @param a the array to sort
+   * @param from the start index (inclusive)
+   * @param count the number of elements to sort
    */
-  public static void sort(short[]a)
+  private static void qsort(float[] array, int from, int count)
+  {
+    // Use an insertion sort on small arrays.
+    if (count <= 7)
+      {
+        for (int i = from + 1; i < from + count; i++)
+          for (int j = i;
+               j > 0 && Float.compare(array[j - 1], array[j]) > 0;
+               j--)
+            {
+              swap(j, j - 1, array);
+            }
+        return;
+      }
+
+    // Determine a good median element.
+    int mid = count / 2;
+    int lo = from;
+    int hi = from + count - 1;
+
+    if (count > 40)
+      { // big arrays, pseudomedian of 9
+        int s = count / 8;
+        lo = med3(lo, lo + s, lo + s + s, array);
+        mid = med3(mid - s, mid, mid + s, array);
+        hi = med3(hi - s - s, hi - s, hi, array);
+      }
+    mid = med3(lo, mid, hi, array);
+
+    int a, b, c, d;
+    int comp;
+
+    // Pull the median element out of the fray, and use it as a pivot.
+    swap(from, mid, array);
+    a = b = from + 1;
+    c = d = hi;
+
+    // Repeatedly move b and c to each other, swapping elements so
+    // that all elements before index b are less than the pivot, and all
+    // elements after index c are greater than the pivot. a and b track
+    // the elements equal to the pivot.
+    while (true)
+      {
+        while (b <= c && (comp = Float.compare(array[b], array[from])) <= 0)
+          {
+            if (comp == 0)
+              {
+                swap(a, b, array);
+                a++;
+              }
+            b++;
+          }
+        while (c >= b && (comp = Float.compare(array[c], array[from])) >= 0)
+          {
+            if (comp == 0)
+              {
+                swap(c, d, array);
+                d--;
+              }
+            c--;
+          }
+        if (b > c)
+          break;
+        swap(b, c, array);
+        b++;
+        c--;
+      }
+
+    // Swap pivot(s) back in place, the recurse on left and right sections.
+    int span;
+    span = Math.min(a - from, b - a);
+    vecswap(from, b - span, span, array);
+
+    span = Math.min(d - c, hi - d - 1);
+    vecswap(b, hi - span + 1, span, array);
+
+    span = b - a;
+    if (span > 1)
+      qsort(array, from, span);
+
+    span = d - c;
+    if (span > 1)
+      qsort(array, hi - span + 1, span);
+  }
+
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the double array to sort
+   */
+  public static void sort(double[] a)
   {
     qsort(a, 0, a.length);
   }
 
-  public static void sort(short[] a, int fromIndex, int toIndex)
+  /**
+   * Performs a stable sort on the elements, arranging them according to their
+   * natural order.
+   *
+   * @param a the double array to sort
+   * @param fromIndex the first index to sort (inclusive)
+   * @param toIndex the last index to sort (exclusive)
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; a.length
+   */
+  public static void sort(double[] a, int fromIndex, int toIndex)
   {
-    qsort(a, fromIndex, toIndex);
+    if (fromIndex > toIndex)
+      throw new IllegalArgumentException();
+    qsort(a, fromIndex, toIndex - fromIndex);
   }
 
-  private static int med3(int a, int b, int c, short[]d)
+  /**
+   * Finds the index of the median of three array elements.
+   *
+   * @param a the first index
+   * @param b the second index
+   * @param c the third index
+   * @param d the array
+   * @return the index (a, b, or c) which has the middle value of the three
+   */
+  private static int med3(int a, int b, int c, double[] d)
   {
-    return d[a] < d[b] ? 
-               (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
-            : (d[b] > d[c] ? b : d[a] > d[c] ? c : a);
+    return (Double.compare(d[a], d[b]) < 0
+            ? (Double.compare(d[b], d[c]) < 0 ? b
+               : Double.compare(d[a], d[c]) < 0 ? c : a)
+            : (Double.compare(d[b], d[c]) > 0 ? b
+               : Double.compare(d[a], d[c]) > 0 ? c : a));
   }
 
-  private static void swap(int i, int j, short[]a)
+  /**
+   * Swaps the elements at two locations of an array
+   *
+   * @param i the first index
+   * @param j the second index
+   * @param a the array
+   */
+  private static void swap(int i, int j, double[] a)
   {
-    short c = a[i];
+    double c = a[i];
     a[i] = a[j];
     a[j] = c;
   }
 
-  private static void qsort(short[]a, int start, int n)
-  {
-    // use an insertion sort on small arrays
-    if (n <= 7)
-      {
-       for (int i = start + 1; i < start + n; i++)
-         for (int j = i; j > 0 && a[j - 1] > a[j]; j--)
-           swap(j, j - 1, a);
-       return;
-      }
-
-    int pm = n / 2;            // small arrays, middle element
-    if (n > 7)
-      {
-       int pl = start;
-       int pn = start + n - 1;
-
-       if (n > 40)
-         {                     // big arrays, pseudomedian of 9
-           int s = n / 8;
-           pl = med3(pl, pl + s, pl + 2 * s, a);
-           pm = med3(pm - s, pm, pm + s, a);
-           pn = med3(pn - 2 * s, pn - s, pn, a);
-         }
-       pm = med3(pl, pm, pn, a);       // mid-size, med of 3
-      }
-
-    int pa, pb, pc, pd, pv;
-    int r;
-
-    pv = start;
-    swap(pv, pm, a);
-    pa = pb = start;
-    pc = pd = start + n - 1;
-
-    for (;;)
-      {
-       while (pb <= pc && (r = a[pb] - a[pv]) <= 0)
-         {
-           if (r == 0)
-             {
-               swap(pa, pb, a);
-               pa++;
-             }
-           pb++;
-         }
-       while (pc >= pb && (r = a[pc] - a[pv]) >= 0)
-         {
-           if (r == 0)
-             {
-               swap(pc, pd, a);
-               pd--;
-             }
-           pc--;
-         }
-       if (pb > pc)
-         break;
-       swap(pb, pc, a);
-       pb++;
-       pc--;
-      }
-    int pn = start + n;
-    int s;
-    s = Math.min(pa - start, pb - pa);
-    vecswap(start, pb - s, s, a);
-    s = Math.min(pd - pc, pn - pd - 1);
-    vecswap(pb, pn - s, s, a);
-    if ((s = pb - pa) > 1)
-      qsort(a, start, s);
-    if ((s = pd - pc) > 1)
-      qsort(a, pn - s, s);
-  }
-
-  private static void vecswap(int i, int j, int n, short[]a)
+  /**
+   * Swaps two ranges of an array.
+   *
+   * @param i the first range start
+   * @param j the second range start
+   * @param n the element count
+   * @param a the array
+   */
+  private static void vecswap(int i, int j, int n, double[] a)
   {
-    for (; n > 0; i++, j++, n--)
+    for ( ; n > 0; i++, j++, n--)
       swap(i, j, a);
   }
 
   /**
-   * The bulk of the work for the object sort routines.  In general,
-   * the code attempts to be simple rather than fast, the idea being
-   * that a good optimising JIT will be able to optimise it better
-   * than I can, and if I try it will make it more confusing for the
-   * JIT.  
+   * Performs a recursive modified quicksort.
+   *
+   * @param a the array to sort
+   * @param from the start index (inclusive)
+   * @param count the number of elements to sort
    */
-  private static void mergeSort(Object[]a, int from, int to, Comparator c)
-  {
-    // First presort the array in chunks of length 6 with insertion sort. 
-    // mergesort would give too much overhead for this length.
-    for (int chunk = from; chunk < to; chunk += 6)
-      {
-       int end = Math.min(chunk + 6, to);
-       for (int i = chunk + 1; i < end; i++)
-         {
-           if (c.compare(a[i - 1], a[i]) > 0)
-             {
-               // not already sorted
-               int j = i;
-               Object elem = a[j];
-               do
-                 {
-                   a[j] = a[j - 1];
-                   j--;
-                 }
-               while (j > chunk && c.compare(a[j - 1], elem) > 0);
-               a[j] = elem;
-             }
-         }
-      }
-
-    int len = to - from;
-    // If length is smaller or equal 6 we are done.
-    if (len <= 6)
-      return;
-
-    Object[]src = a;
-    Object[]dest = new Object[len];
-    Object[]t = null;          // t is used for swapping src and dest
-
-    // The difference of the fromIndex of the src and dest array.
-    int srcDestDiff = -from;
-
-    // The merges are done in this loop
-    for (int size = 6; size < len; size <<= 1)
-      {
-       for (int start = from; start < to; start += size << 1)
-         {
-           // mid ist the start of the second sublist;
-           // end the start of the next sublist (or end of array).
-           int mid = start + size;
-           int end = Math.min(to, mid + size);
-
-           // The second list is empty or the elements are already in
-           // order - no need to merge
-           if (mid >= end || c.compare(src[mid - 1], src[mid]) <= 0)
-             {
-               System.arraycopy(src, start,
-                                dest, start + srcDestDiff, end - start);
-
-               // The two halves just need swapping - no need to merge
-             }
-           else if (c.compare(src[start], src[end - 1]) > 0)
-             {
-               System.arraycopy(src, start,
-                                dest, end - size + srcDestDiff, size);
-               System.arraycopy(src, mid,
-                                dest, start + srcDestDiff, end - mid);
-
-             }
-           else
-             {
-               // Declare a lot of variables to save repeating
-               // calculations.  Hopefully a decent JIT will put these
-               // in registers and make this fast
-               int p1 = start;
-               int p2 = mid;
-               int i = start + srcDestDiff;
-
-               // The main merge loop; terminates as soon as either
-               // half is ended
-               while (p1 < mid && p2 < end)
-                 {
-                   dest[i++] =
-                     src[c.compare(src[p1], src[p2]) <= 0 ? p1++ : p2++];
-                 }
-
-               // Finish up by copying the remainder of whichever half
-               // wasn't finished.
-               if (p1 < mid)
-                 System.arraycopy(src, p1, dest, i, mid - p1);
-               else
-                 System.arraycopy(src, p2, dest, i, end - p2);
-             }
-         }
-       // swap src and dest ready for the next merge
-       t = src;
-       src = dest;
-       dest = t;
-       from += srcDestDiff;
-       to += srcDestDiff;
-       srcDestDiff = -srcDestDiff;
-      }
-
-    // make sure the result ends up back in the right place.  Note
-    // that src and dest may have been swapped above, so src 
-    // contains the sorted array.
-    if (src != a)
-      {
-       // Note that from == 0.
-       System.arraycopy(src, 0, a, srcDestDiff, to);
-      }
+  private static void qsort(double[] array, int from, int count)
+  {
+    // Use an insertion sort on small arrays.
+    if (count <= 7)
+      {
+        for (int i = from + 1; i < from + count; i++)
+          for (int j = i;
+               j > 0 && Double.compare(array[j - 1], array[j]) > 0;
+               j--)
+            {
+              swap(j, j - 1, array);
+            }
+        return;
+      }
+
+    // Determine a good median element.
+    int mid = count / 2;
+    int lo = from;
+    int hi = from + count - 1;
+
+    if (count > 40)
+      { // big arrays, pseudomedian of 9
+        int s = count / 8;
+        lo = med3(lo, lo + s, lo + s + s, array);
+        mid = med3(mid - s, mid, mid + s, array);
+        hi = med3(hi - s - s, hi - s, hi, array);
+      }
+    mid = med3(lo, mid, hi, array);
+
+    int a, b, c, d;
+    int comp;
+
+    // Pull the median element out of the fray, and use it as a pivot.
+    swap(from, mid, array);
+    a = b = from + 1;
+    c = d = hi;
+
+    // Repeatedly move b and c to each other, swapping elements so
+    // that all elements before index b are less than the pivot, and all
+    // elements after index c are greater than the pivot. a and b track
+    // the elements equal to the pivot.
+    while (true)
+      {
+        while (b <= c && (comp = Double.compare(array[b], array[from])) <= 0)
+          {
+            if (comp == 0)
+              {
+                swap(a, b, array);
+                a++;
+              }
+            b++;
+          }
+        while (c >= b && (comp = Double.compare(array[c], array[from])) >= 0)
+          {
+            if (comp == 0)
+              {
+                swap(c, d, array);
+                d--;
+              }
+            c--;
+          }
+        if (b > c)
+          break;
+        swap(b, c, array);
+        b++;
+        c--;
+      }
+
+    // Swap pivot(s) back in place, the recurse on left and right sections.
+    int span;
+    span = Math.min(a - from, b - a);
+    vecswap(from, b - span, span, array);
+
+    span = Math.min(d - c, hi - d - 1);
+    vecswap(b, hi - span + 1, span, array);
+
+    span = b - a;
+    if (span > 1)
+      qsort(array, from, span);
+
+    span = d - c;
+    if (span > 1)
+      qsort(array, hi - span + 1, span);
   }
 
   /**
@@ -1972,18 +2136,19 @@ public class Arrays
    * guaranteed to be stable, that is, equal elements will not be reordered.
    * The sort algorithm is a mergesort with the merge omitted if the last
    * element of one half comes before the first element of the other half. This
-   * algorithm gives guaranteed O(nlog(n)) time, at the expense of making a
+   * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a
    * copy of the array.
    *
    * @param a the array to be sorted
-   * @exception ClassCastException if any two elements are not mutually
-   *   comparable
-   * @exception NullPointerException if an element is null (since
-   *   null.compareTo cannot work)
+   * @throws ClassCastException if any two elements are not mutually
+   *         comparable
+   * @throws NullPointerException if an element is null (since
+   *         null.compareTo cannot work)
+   * @see Comparable
    */
-  public static void sort(Object[]a)
+  public static void sort(Object[] a)
   {
-    mergeSort(a, 0, a.length, defaultComparator);
+    sort(a, 0, a.length, null);
   }
 
   /**
@@ -1991,17 +2156,20 @@ public class Arrays
    * guaranteed to be stable, that is, equal elements will not be reordered.
    * The sort algorithm is a mergesort with the merge omitted if the last
    * element of one half comes before the first element of the other half. This
-   * algorithm gives guaranteed O(nlog(n)) time, at the expense of making a
+   * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a
    * copy of the array.
    *
    * @param a the array to be sorted
-   * @param c a Comparator to use in sorting the array
-   * @exception ClassCastException if any two elements are not mutually
-   *   comparable by the Comparator provided
+   * @param c a Comparator to use in sorting the array; or null to indicate
+   *        the elements' natural order
+   * @throws ClassCastException if any two elements are not mutually
+   *         comparable by the Comparator provided
+   * @throws NullPointerException if a null element is compared with natural
+   *         ordering (only possible when c is null)
    */
-  public static void sort(Object[]a, Comparator c)
+  public static void sort(Object[] a, Comparator c)
   {
-    mergeSort(a, 0, a.length, c);
+    sort(a, 0, a.length, c);
   }
 
   /**
@@ -2009,24 +2177,23 @@ public class Arrays
    * guaranteed to be stable, that is, equal elements will not be reordered.
    * The sort algorithm is a mergesort with the merge omitted if the last
    * element of one half comes before the first element of the other half. This
-   * algorithm gives guaranteed O(nlog(n)) time, at the expense of making a
+   * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a
    * copy of the array.
    *
    * @param a the array to be sorted
-   * @param fromIndex the index of the first element to be sorted.
-   * @param toIndex the index of the last element to be sorted plus one.
-   * @exception ClassCastException if any two elements are not mutually
-   *   comparable by the Comparator provided
-   * @exception ArrayIndexOutOfBoundsException, if fromIndex and toIndex
-   *   are not in range.
-   * @exception IllegalArgumentException if fromIndex > toIndex
+   * @param fromIndex the index of the first element to be sorted
+   * @param toIndex the index of the last element to be sorted plus one
+   * @throws ClassCastException if any two elements are not mutually
+   *         comparable
+   * @throws NullPointerException if an element is null (since
+   *         null.compareTo cannot work)
+   * @throws ArrayIndexOutOfBoundsException, if fromIndex and toIndex
+   *         are not in range.
+   * @throws IllegalArgumentException if fromIndex > toIndex
    */
-  public static void sort(Object[]a, int fromIndex, int toIndex)
+  public static void sort(Object[] a, int fromIndex, int toIndex)
   {
-    if (fromIndex > toIndex)
-      throw new IllegalArgumentException("fromIndex " + fromIndex
-                                        + " > toIndex " + toIndex);
-    mergeSort(a, fromIndex, toIndex, defaultComparator);
+    sort(a, fromIndex, toIndex, null);
   }
 
   /**
@@ -2034,56 +2201,195 @@ public class Arrays
    * guaranteed to be stable, that is, equal elements will not be reordered.
    * The sort algorithm is a mergesort with the merge omitted if the last
    * element of one half comes before the first element of the other half. This
-   * algorithm gives guaranteed O(nlog(n)) time, at the expense of making a
+   * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a
    * copy of the array.
    *
    * @param a the array to be sorted
-   * @param fromIndex the index of the first element to be sorted.
-   * @param toIndex the index of the last element to be sorted plus one.
-   * @param c a Comparator to use in sorting the array
-   * @exception ClassCastException if any two elements are not mutually
-   *   comparable by the Comparator provided
-   * @exception ArrayIndexOutOfBoundsException, if fromIndex and toIndex
-   *   are not in range.
-   * @exception IllegalArgumentException if fromIndex > toIndex
+   * @param fromIndex the index of the first element to be sorted
+   * @param toIndex the index of the last element to be sorted plus one
+   * @param c a Comparator to use in sorting the array; or null to indicate
+   *        the elements' natural order
+   * @throws ClassCastException if any two elements are not mutually
+   *         comparable by the Comparator provided
+   * @throws ArrayIndexOutOfBoundsException, if fromIndex and toIndex
+   *         are not in range.
+   * @throws IllegalArgumentException if fromIndex > toIndex
+   * @throws NullPointerException if a null element is compared with natural
+   *         ordering (only possible when c is null)
    */
-  public static void sort(Object[]a, int fromIndex, int toIndex, Comparator c)
+  public static void sort(Object[] a, int fromIndex, int toIndex, Comparator c)
   {
     if (fromIndex > toIndex)
       throw new IllegalArgumentException("fromIndex " + fromIndex
-                                        + " > toIndex " + toIndex);
-    mergeSort(a, fromIndex, toIndex, c);
+                                         + " > toIndex " + toIndex);
+
+    // In general, the code attempts to be simple rather than fast, the
+    // idea being that a good optimising JIT will be able to optimise it
+    // better than I can, and if I try it will make it more confusing for
+    // the JIT. First presort the array in chunks of length 6 with insertion
+    // sort. A mergesort would give too much overhead for this length.
+    for (int chunk = fromIndex; chunk < toIndex; chunk += 6)
+      {
+        int end = Math.min(chunk + 6, toIndex);
+        for (int i = chunk + 1; i < end; i++)
+          {
+            if (Collections.compare(a[i - 1], a[i], c) > 0)
+              {
+                // not already sorted
+                int j = i;
+                Object elem = a[j];
+                do
+                  {
+                    a[j] = a[j - 1];
+                    j--;
+                  }
+                while (j > chunk
+                       && Collections.compare(a[j - 1], elem, c) > 0);
+                a[j] = elem;
+              }
+          }
+      }
+
+    int len = toIndex - fromIndex;
+    // If length is smaller or equal 6 we are done.
+    if (len <= 6)
+      return;
+
+    Object[] src = a;
+    Object[] dest = new Object[len];
+    Object[] t = null; // t is used for swapping src and dest
+
+    // The difference of the fromIndex of the src and dest array.
+    int srcDestDiff = -fromIndex;
+
+    // The merges are done in this loop
+    for (int size = 6; size < len; size <<= 1)
+      {
+        for (int start = fromIndex; start < toIndex; start += size << 1)
+          {
+            // mid is the start of the second sublist;
+            // end the start of the next sublist (or end of array).
+            int mid = start + size;
+            int end = Math.min(toIndex, mid + size);
+
+            // The second list is empty or the elements are already in
+            // order - no need to merge
+            if (mid >= end
+                || Collections.compare(src[mid - 1], src[mid], c) <= 0)
+              {
+                System.arraycopy(src, start,
+                                 dest, start + srcDestDiff, end - start);
+
+                // The two halves just need swapping - no need to merge
+              }
+            else if (Collections.compare(src[start], src[end - 1], c) > 0)
+              {
+                System.arraycopy(src, start,
+                                 dest, end - size + srcDestDiff, size);
+                System.arraycopy(src, mid,
+                                 dest, start + srcDestDiff, end - mid);
+
+              }
+            else
+              {
+                // Declare a lot of variables to save repeating
+                // calculations.  Hopefully a decent JIT will put these
+                // in registers and make this fast
+                int p1 = start;
+                int p2 = mid;
+                int i = start + srcDestDiff;
+
+                // The main merge loop; terminates as soon as either
+                // half is ended
+                while (p1 < mid && p2 < end)
+                  {
+                    dest[i++] =
+                      src[(Collections.compare(src[p1], src[p2], c) <= 0
+                           ? p1++ : p2++)];
+                  }
+
+                // Finish up by copying the remainder of whichever half
+                // wasn't finished.
+                if (p1 < mid)
+                  System.arraycopy(src, p1, dest, i, mid - p1);
+                else
+                  System.arraycopy(src, p2, dest, i, end - p2);
+              }
+          }
+        // swap src and dest ready for the next merge
+        t = src;
+        src = dest;
+        dest = t;
+        fromIndex += srcDestDiff;
+        toIndex += srcDestDiff;
+        srcDestDiff = -srcDestDiff;
+      }
+
+    // make sure the result ends up back in the right place.  Note
+    // that src and dest may have been swapped above, so src
+    // contains the sorted array.
+    if (src != a)
+      {
+        // Note that fromIndex == 0.
+        System.arraycopy(src, 0, a, srcDestDiff, toIndex);
+      }
   }
 
   /**
    * Returns a list "view" of the specified array. This method is intended to
    * make it easy to use the Collections API with existing array-based APIs and
-   * programs.
+   * programs. Changes in the list or the array show up in both places. The
+   * list does not support element addition or removal, but does permit
+   * value modification. The returned list implements both Serializable and
+   * RandomAccess.
    *
    * @param a the array to return a view of
-   * @returns a fixed-size list, changes to which "write through" to the array
+   * @return a fixed-size list, changes to which "write through" to the array
+   * @see Serializable
+   * @see RandomAccess
+   * @see Arrays.ArrayList
    */
-  public static List asList(final Object[]a)
+  public static List asList(final Object[] a)
   {
-    if (a == null)
-      {
-       throw new NullPointerException();
-      }
-
-    return new ListImpl(a);
+    return new Arrays.ArrayList(a);
   }
 
-
   /**
-   * Inner class used by asList(Object[]) to provide a list interface
-   * to an array. The methods are all simple enough to be self documenting.
-   * Note: When Sun fully specify serialized forms, this class will have to
-   * be renamed.
+   * Inner class used by {@link #asList(Object[])} to provide a list interface
+   * to an array. The name, though it clashes with java.util.ArrayList, is
+   * Sun's choice for Serialization purposes. Element addition and removal
+   * is prohibited, but values can be modified.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   * @status updated to 1.4
    */
-  private static class ListImpl extends AbstractList
-  {
-    ListImpl(Object[]a)
+  private static final class ArrayList extends AbstractList
+    implements Serializable, RandomAccess
+  {
+    // We override the necessary methods, plus others which will be much
+    // more efficient with direct iteration rather than relying on iterator().
+
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -2764017481108945198L;
+
+    /**
+     * The array we are viewing.
+     * @serial the array
+     */
+    private final Object[] a;
+
+    /**
+     * Construct a list view of the array.
+     * @param a the array to view
+     * @throws NullPointerException if a is null
+     */
+    ArrayList(Object[] a)
     {
+      // We have to explicitly check.
+      if (a == null)
+        throw new NullPointerException();
       this.a = a;
     }
 
@@ -2104,6 +2410,45 @@ public class Arrays
       return old;
     }
 
-    private Object[] a;
+    public boolean contains(Object o)
+    {
+      return lastIndexOf(o) >= 0;
+    }
+
+    public int indexOf(Object o)
+    {
+      int size = a.length;
+      for (int i = 0; i < size; i++)
+        if (equals(o, a[i]))
+          return i;
+      return -1;
+    }
+
+    public int lastIndexOf(Object o)
+    {
+      int i = a.length;
+      while (--i >= 0)
+        if (equals(o, a[i]))
+          return i;
+      return -1;
+    }
+
+    public Object[] toArray()
+    {
+      return (Object[]) a.clone();
+    }
+
+    public Object[] toArray(Object[] array)
+    {
+      int size = a.length;
+      if (array.length < size)
+        array = (Object[])
+          Array.newInstance(array.getClass().getComponentType(), size);
+      else if (array.length > size)
+        array[size] = null;
+
+      System.arraycopy(a, 0, array, 0, size);
+      return array;
+    }
   }
 }
index f858cb4ebd979cbfb19be787d5de6cb95477a064..48fcc146664ab32f87b8e519cd9eab077f9b9c36 100644 (file)
@@ -1,6 +1,6 @@
 /* BasicMapEntry.java -- a class providing a plain-vanilla implementation of
    the Map.Entry interface; could be used anywhere in java.util
-   Copyright (C) 1998, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -29,52 +29,113 @@ executable file might be covered by the GNU General Public License. */
 package java.util;
 
 /**
- * A class which implements Map.Entry. It is shared by HashMap, TreeMap, and
- * Hashtable.
+ * A class which implements Map.Entry. It is shared by HashMap, TreeMap,
+ * Hashtable, and Collections. It is not specified by the JDK, but makes
+ * life much easier.
  *
- * @author      Jon Zeppieri
+ * @author Jon Zeppieri
+ * @author Eric Blake <ebb9@email.byu.edu>
  */
 class BasicMapEntry implements Map.Entry
 {
+  /**
+   * The key. Package visible for direct manipulation.
+   */
   Object key;
+
+  /**
+   * The value. Package visible for direct manipulation.
+   */
   Object value;
 
+  /**
+   * Basic constructor initializes the fields.
+   * @param newKey the key
+   * @param newValue the value
+   */
   BasicMapEntry(Object newKey, Object newValue)
   {
     key = newKey;
     value = newValue;
   }
 
+  /**
+   * Compares the specified object with this entry. Returns true only if
+   * the object is a mapping of identical key and value. In other words,
+   * this must be:
+   * <pre>
+   * (o instanceof Map.Entry) &&
+   * (getKey() == null ? ((HashMap) o).getKey() == null
+   *                   : getKey().equals(((HashMap) o).getKey())) &&
+   * (getValue() == null ? ((HashMap) o).getValue() == null
+   *                   : getValue().equals(((HashMap) o).getValue()))
+   * </pre>
+   *
+   * @param o the object to compare
+   * @return true if it is equal
+   */
   public final boolean equals(Object o)
   {
-    if (!(o instanceof Map.Entry))
+    if (! (o instanceof Map.Entry))
       return false;
+    // Optimize for our own entries.
+    if (o instanceof BasicMapEntry)
+      {
+        BasicMapEntry e = (BasicMapEntry) o;
+        return (AbstractCollection.equals(key, e.key)
+                && AbstractCollection.equals(value, e.value));
+      }
     Map.Entry e = (Map.Entry) o;
-    return (key == null ? e.getKey() == null : key.equals(e.getKey())
-            && value == null ? e.getValue() == null 
-                            : value.equals(e.getValue()));
+    return (AbstractCollection.equals(key, e.getKey())
+            && AbstractCollection.equals(value, e.getValue()));
   }
 
+  /**
+   * Get the key corresponding to this entry.
+   *
+   * @return the key
+   */
   public final Object getKey()
   {
     return key;
   }
 
+  /**
+   * Get the value corresponding to this entry. If you already called
+   * Iterator.remove(), the behavior undefined, but in this case it works.
+   *
+   * @return the value
+   */
   public final Object getValue()
   {
     return value;
   }
 
+  /**
+   * Returns the hash code of the entry.  This is defined as the exclusive-or
+   * of the hashcodes of the key and value (using 0 for null). In other
+   * words, this must be:
+   * <pre>
+   *  (getKey() == null ? 0 : getKey().hashCode()) ^
+   *  (getValue() == null ? 0 : getValue().hashCode())
+   * </pre>
+   *
+   * @return the hash code
+   */
   public final int hashCode()
   {
-    int kc = (key == null ? 0 : key.hashCode());
-    int vc = (value == null ? 0 : value.hashCode());
-    return kc ^ vc;
+    return (AbstractCollection.hashCode(key)
+            ^ AbstractCollection.hashCode(value));
   }
 
-  /** 
-   * sets the value of this Map.Entry. Note that this is overriden by 
-   * Hashtable.Entry, which does not permit a null value.
+  /**
+   * Replaces the value with the specified object. This writes through
+   * to the map, unless you have already called Iterator.remove(). It
+   * may be overridden to restrict a null value.
+   *
+   * @param newVal the new value to store
+   * @return the old value
+   * @throws NullPointerException if the map forbids null values
    */
   public Object setValue(Object newVal)
   {
@@ -83,6 +144,12 @@ class BasicMapEntry implements Map.Entry
     return r;
   }
 
+  /**
+   * This provides a string representation of the entry. It is of the form
+   * "key=value", where string concatenation is used on key and value.
+   *
+   * @return the string representation
+   */
   public final String toString()
   {
     return key + "=" + value;
index 1da187558dd4b35a6eacf89d71368db7f4b65f25..366c005333318c8da48950ef22f1595040ef796c 100644 (file)
@@ -1,6 +1,5 @@
-// BitSet - A vector of bits.
-
-/* Copyright (C) 1998, 1999, 2000  Free Software Foundation
+/* BitSet.java -- A vector of bits.
+   Copyright (C) 1998, 1999, 2000, 2001  Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -50,22 +49,32 @@ import java.io.Serializable;
  * while another thread is simultaneously modifying it, the results are
  * undefined.
  *
- * @specnote Historically, there has been some confusion as to whether or not 
- *           this class should be synchronized. From an efficiency perspective,
- *           it is very undesirable to synchronize it because multiple locks 
- *           and explicit lock ordering are required to safely synchronize some
- *           methods. The JCL 1.2 supplement book specifies that as of JDK 
- *           1.2, the class is no longer synchronized.
- *
  * @author Jochen Hoenicke
  * @author Tom Tromey <tromey@cygnus.com>
- * @date October 23, 1998.
- * @status API complete to JDK 1.3.
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @status updated to 1.4
  */
 public class BitSet implements Cloneable, Serializable
 {
   /**
-   * Create a new empty bit set.
+   * Compatible with JDK 1.0.
+   */
+  private static final long serialVersionUID = 7997698588986878753L;
+
+  /**
+   * A common mask.
+   */
+  private static final int LONG_MASK = 0x3f;
+
+  /**
+   * The actual bits.
+   * @serial the i'th bit is in bits[i/64] at position i%64 (where position
+   *         0 is the least significant).
+   */
+  private long[] bits;
+
+  /**
+   * Create a new empty bit set. All bits are initially false.
    */
   public BitSet()
   {
@@ -75,18 +84,15 @@ public class BitSet implements Cloneable, Serializable
   /**
    * Create a new empty bit set, with a given size.  This
    * constructor reserves enough space to represent the integers
-   * from <code>0</code> to <code>nbits-1</code>.  
-   * @param nbits the initial size of the bit set.
-   * @throws NegativeArraySizeException if the specified initial
-   * size is negative.  
-   * @require nbits >= 0
+   * from <code>0</code> to <code>nbits-1</code>.
+   *
+   * @param nbits the initial size of the bit set
+   * @throws NegativeArraySizeException if nbits &lt; 0
    */
   public BitSet(int nbits)
   {
-    if (nbits < 0)
-      throw new NegativeArraySizeException();
-    int length = nbits / 64;
-    if (nbits % 64 != 0)
+    int length = nbits >>> 6;
+    if ((nbits & LONG_MASK) != 0)
       ++length;
     bits = new long[length];
   }
@@ -95,8 +101,9 @@ public class BitSet implements Cloneable, Serializable
    * Performs the logical AND operation on this bit set and the
    * given <code>set</code>.  This means it builds the intersection
    * of the two sets.  The result is stored into this bit set.
-   * @param set the second bit set.
-   * @require set != null
+   *
+   * @param set the second bit set
+   * @throws NullPointerException if set is null
    */
   public void and(BitSet bs)
   {
@@ -104,63 +111,143 @@ public class BitSet implements Cloneable, Serializable
     int i;
     for (i = 0; i < max; ++i)
       bits[i] &= bs.bits[i];
-    for (; i < bits.length; ++i)
-      bits[i] = 0;
+    while (i < bits.length)
+      bits[i++] = 0;
   }
 
   /**
    * Performs the logical AND operation on this bit set and the
    * complement of the given <code>set</code>.  This means it
    * selects every element in the first set, that isn't in the
-   * second set.  The result is stored into this bit set.  
-   * @param set the second bit set.  
-   * @require set != null
-   * @since JDK1.2
+   * second set.  The result is stored into this bit set.
+   *
+   * @param set the second bit set
+   * @throws NullPointerException if set is null
+   * @since 1.2
    */
   public void andNot(BitSet bs)
   {
-    int max = Math.min(bits.length, bs.bits.length);
-    int i;
-    for (i = 0; i < max; ++i)
+    int i = Math.min(bits.length, bs.bits.length);
+    while (--i >= 0)
       bits[i] &= ~bs.bits[i];
   }
 
+  /**
+   * Returns the number of bits set to true.
+   *
+   * @return the number of true bits
+   * @since 1.4
+   */
+  public int cardinality()
+  {
+    int card = 0;
+    for (int i = bits.length - 1; i >= 0; i--)
+      {
+        long a = bits[i];
+        // Take care of common cases.
+        if (a == 0)
+          continue;
+        if (a == -1)
+          {
+            card += 64;
+            continue;
+          }
+
+        // Successively collapse alternating bit groups into a sum.
+        a = ((a >> 1) & 0x5555555555555555L) + (a & 0x5555555555555555L);
+        a = ((a >> 2) & 0x3333333333333333L) + (a & 0x3333333333333333L);
+        int b = (int) ((a >>> 32) + a);
+        b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f);
+        b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff);
+        card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff);
+      }
+    return card;
+  }
+
+  /**
+   * Sets all bits in the set to false.
+   *
+   * @since 1.4
+   */
+  public void clear()
+  {
+    Arrays.fill(bits, 0);
+  }
+
   /**
    * Removes the integer <code>bitIndex</code> from this set. That is
    * the corresponding bit is cleared.  If the index is not in the set,
    * this method does nothing.
-   * @param bitIndex a non-negative integer.
-   * @exception ArrayIndexOutOfBoundsException if the specified bit index
-   * is negative.
-   * @require bitIndex >= 0
+   *
+   * @param bitIndex a non-negative integer
+   * @throws IndexOutOfBoundsException if bitIndex &lt; 0
    */
   public void clear(int pos)
   {
-    if (pos < 0)
-      throw new IndexOutOfBoundsException();
-    int bit = pos % 64;
-    int offset = pos / 64;
+    int offset = pos >>> 6;
     ensure(offset);
-    bits[offset] &= ~(1L << bit);
+    // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+    // so we'll just let that be our exception.
+    bits[offset] &= ~(1L << pos);
+  }
+
+  /**
+   * Sets the bits between from (inclusive) and to (exclusive) to false.
+   *
+   * @param from the start range (inclusive)
+   * @param to the end range (exclusive)
+   * @throws IndexOutOfBoundsException if from &lt; 0 || from &gt; to
+   * @since 1.4
+   */
+  public void clear(int from, int to)
+  {
+    if (from < 0 || from > to)
+      throw new IndexOutOfBoundsException();
+    if (from == to)
+      return;
+    int lo_offset = from >>> 6;
+    int hi_offset = to >>> 6;
+    ensure(hi_offset);
+    if (lo_offset == hi_offset)
+      {
+        bits[hi_offset] &= ((1L << from) - 1) | (-1L << to);
+        return;
+      }
+
+    bits[lo_offset] &= (1L << from) - 1;
+    bits[hi_offset] &= -1L << to;
+    for (int i = lo_offset + 1; i < hi_offset; i++)
+      bits[i] = 0;
   }
 
   /**
    * Create a clone of this bit set, that is an instance of the same
    * class and contains the same elements.  But it doesn't change when
    * this bit set changes.
+   *
    * @return the clone of this object.
    */
   public Object clone()
   {
-    BitSet bs = new BitSet(bits.length * 64);
-    System.arraycopy(bits, 0, bs.bits, 0, bits.length);
-    return bs;
+    try
+      {
+        BitSet bs = (BitSet) super.clone();
+        bs.bits = (long[]) bits.clone();
+        return bs;
+      }
+    catch (CloneNotSupportedException e)
+      {
+        // Impossible to get here.
+        return null;
+      }
   }
 
   /**
    * Returns true if the <code>obj</code> is a bit set that contains
    * exactly the same elements as this bit set, otherwise false.
-   * @return true if obj equals this bit set.
+   *
+   * @param obj the object to compare to
+   * @return true if obj equals this bit set
    */
   public boolean equals(Object obj)
   {
@@ -171,42 +258,124 @@ public class BitSet implements Cloneable, Serializable
     int i;
     for (i = 0; i < max; ++i)
       if (bits[i] != bs.bits[i])
-       return false;
+        return false;
     // If one is larger, check to make sure all extra bits are 0.
     for (int j = i; j < bits.length; ++j)
       if (bits[j] != 0)
-       return false;
+        return false;
     for (int j = i; j < bs.bits.length; ++j)
       if (bs.bits[j] != 0)
-       return false;
+        return false;
     return true;
   }
 
   /**
-   * Returns true if the integer <code>bitIndex</code> is in this bit
-   * set, otherwise false.
-   * @param bitIndex a non-negative integer
-   * @return the value of the bit at the specified index.
-   * @exception ArrayIndexOutOfBoundsException if the specified bit index
-   * is negative.
-   * @require bitIndex >= 0
+   * Sets the bit at the index to the opposite value.
+   *
+   * @param index the index of the bit
+   * @throws IndexOutOfBoundsException if index is negative
+   * @since 1.4
    */
-  public boolean get(int pos)
+  public void flip(int index)
   {
-    if (pos < 0)
+    int offset = index >>> 6;
+    ensure(offset);
+    // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+    // so we'll just let that be our exception.
+    bits[offset] ^= 1L << index;
+  }
+
+  /**
+   * Sets a range of bits to the opposite value.
+   *
+   * @param from the low index (inclusive)
+   * @param to the high index (exclusive)
+   * @throws IndexOutOfBoundsException if from &gt; to || from &lt; 0
+   * @since 1.4
+   */
+  public void flip(int from, int to)
+  {
+    if (from < 0 || from > to)
       throw new IndexOutOfBoundsException();
+    if (from == to)
+      return;
+    int lo_offset = from >>> 6;
+    int hi_offset = to >>> 6;
+    ensure(hi_offset);
+    if (lo_offset == hi_offset)
+      {
+        bits[hi_offset] ^= (-1L << from) & ((1L << to) - 1);
+        return;
+      }
 
-    int bit = pos % 64;
-    int offset = pos / 64;
+    bits[lo_offset] ^= -1L << from;
+    bits[hi_offset] ^= (1L << to) - 1;
+    for (int i = lo_offset + 1; i < hi_offset; i++)
+      bits[i] ^= -1;
+  }
 
+  /**
+   * Returns true if the integer <code>bitIndex</code> is in this bit
+   * set, otherwise false.
+   *
+   * @param pos a non-negative integer
+   * @return the value of the bit at the specified index
+   * @throws IndexOutOfBoundsException if the index is negative
+   */
+  public boolean get(int pos)
+  {
+    int offset = pos >>> 6;
     if (offset >= bits.length)
       return false;
+    // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+    // so we'll just let that be our exception.
+    return (bits[offset] & (1L << pos)) != 0;
+  }
+
+  /**
+   * Returns a new <code>BitSet</code> composed of a range of bits from
+   * this one.
+   *
+   * @param from the low index (inclusive)
+   * @param to the high index (exclusive)
+   * @throws IndexOutOfBoundsException if from &gt; to || from &lt; 0
+   * @since 1.4
+   */
+  public BitSet get(int from, int to)
+  {
+    if (from < 0 || from > to)
+      throw new IndexOutOfBoundsException();
+    BitSet bs = new BitSet(to - from);
+    int lo_offset = from >>> 6;
+    if (lo_offset >= bits.length)
+      return bs;
+
+    int lo_bit = from & LONG_MASK;
+    int hi_offset = to >>> 6;
+    if (lo_bit == 0)
+      {
+        int len = Math.min(hi_offset - lo_offset + 1, bits.length - lo_offset);
+        System.arraycopy(bits, lo_offset, bs.bits, 0, len);
+        if (hi_offset < bits.length)
+          bs.bits[hi_offset - lo_offset] &= (1L << to) - 1;
+        return bs;
+      }
 
-    return (bits[offset] & (1L << bit)) == 0 ? false : true;
+    int len = Math.min(hi_offset, bits.length - 1);
+    int reverse = ~lo_bit;
+    int i;
+    for (i = 0; lo_offset < len; lo_offset++, i++)
+      bs.bits[i] = ((bits[lo_offset] >>> lo_bit)
+                    | (bits[lo_offset + 1] << reverse));
+    if ((to & LONG_MASK) > lo_bit)
+      bs.bits[i++] = bits[lo_offset] >>> lo_bit;
+    if (hi_offset < bits.length)
+      bs.bits[i - 1] &= (1L << (to - from)) - 1;
+    return bs;
   }
 
   /**
-   * Returns a hash code value for this bit set.  The hash code of 
+   * Returns a hash code value for this bit set.  The hash code of
    * two bit sets containing the same integers is identical.  The algorithm
    * used to compute it is as follows:
    *
@@ -233,21 +402,55 @@ public class BitSet implements Cloneable, Serializable
    * </pre>
    *
    * Note that the hash code values changes, if the set is changed.
+   *
    * @return the hash code value for this bit set.
    */
   public int hashCode()
   {
     long h = 1234;
-    for (int i = bits.length - 1; i >= 0; --i)
-      h ^= bits[i] * (i + 1);
+    for (int i = bits.length; i > 0; )
+      h ^= i * bits[--i];
     return (int) ((h >> 32) ^ h);
   }
 
+  /**
+   * Returns true if the specified BitSet and this one share at least one
+   * common true bit.
+   *
+   * @param set the set to check for intersection
+   * @return true if the sets intersect
+   * @throws NullPointerException if set is null
+   * @since 1.4
+   */
+  public boolean intersects(BitSet set)
+  {
+    int i = Math.min(bits.length, set.bits.length);
+    while (--i >= 0)
+      if ((bits[i] & set.bits[i]) != 0)
+        return true;
+    return false;
+  }
+
+  /**
+   * Returns true if this set contains no true bits.
+   *
+   * @return true if all bits are false
+   * @since 1.4
+   */
+  public boolean isEmpty()
+  {
+    for (int i = bits.length - 1; i >= 0; i--)
+      if (bits[i] != 0)
+        return false;
+    return true;
+  }
+
   /**
    * Returns the logical number of bits actually used by this bit
    * set.  It returns the index of the highest set bit plus one.
    * Note that this method doesn't return the number of set bits.
-   * @return the index of the highest set bit plus one.  
+   *
+   * @return the index of the highest set bit plus one.
    */
   public int length()
   {
@@ -266,54 +469,186 @@ public class BitSet implements Cloneable, Serializable
     // b >= 0 checks if the highest bit is zero.
     while (b >= 0)
       {
-       --len;
-       b <<= 1;
+        --len;
+        b <<= 1;
       }
 
     return len;
   }
 
+  /**
+   * Returns the index of the next false bit, from the specified bit
+   * (inclusive).
+   *
+   * @param from the start location
+   * @return the first false bit
+   * @throws IndexOutOfBoundsException if from is negative
+   * @since 1.4
+   */
+  public int nextClearBit(int from)
+  {
+    int offset = from >>> 6;
+    long mask = 1L << from;
+    while (offset < bits.length)
+      {
+        // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+        // so we'll just let that be our exception.
+        long h = bits[offset];
+        do
+          {
+            if ((h & mask) == 0)
+              return from;
+            mask <<= 1;
+            from++;
+          }
+        while (mask != 0);
+        mask = 1;
+        offset++;
+      }
+    return from;
+  }
+
+  /**
+   * Returns the index of the next true bit, from the specified bit
+   * (inclusive). If there is none, -1 is returned. You can iterate over
+   * all true bits with this loop:<br>
+   * <pre>
+   * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
+   *   { // operate on i here }
+   * </pre>
+   *
+   * @param from the start location
+   * @return the first true bit, or -1
+   * @throws IndexOutOfBoundsException if from is negative
+   * @since 1.4
+   */
+  public int nextSetBit(int from)
+  {
+    int offset = from >>> 6;
+    long mask = 1L << from;
+    while (offset < bits.length)
+      {
+        // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+        // so we'll just let that be our exception.
+        long h = bits[offset];
+        do
+          {
+            if ((h & mask) != 0)
+              return from;
+            mask <<= 1;
+            from++;
+          }
+        while (mask != 0);
+        mask = 1;
+        offset++;
+      }
+    return -1;
+  }
+
   /**
    * Performs the logical OR operation on this bit set and the
    * given <code>set</code>.  This means it builds the union
    * of the two sets.  The result is stored into this bit set, which
    * grows as necessary.
-   * @param set the second bit set.
-   * @exception OutOfMemoryError if the current set can't grow.
-   * @require set != null
+   *
+   * @param bs the second bit set
+   * @throws NullPointerException if bs is null
    */
   public void or(BitSet bs)
   {
     ensure(bs.bits.length - 1);
-    int i;
-    for (i = 0; i < bs.bits.length; ++i)
+    for (int i = bs.bits.length - 1; i >= 0; i--)
       bits[i] |= bs.bits[i];
   }
 
   /**
-   * Add the integer <code>bitIndex</code> to this set.  That is 
+   * Add the integer <code>bitIndex</code> to this set.  That is
    * the corresponding bit is set to true.  If the index was already in
    * the set, this method does nothing.  The size of this structure
    * is automatically increased as necessary.
-   * @param bitIndex a non-negative integer.
-   * @exception ArrayIndexOutOfBoundsException if the specified bit index
-   * is negative.
-   * @require bitIndex >= 0
+   *
+   * @param pos a non-negative integer.
+   * @throws IndexOutOfBoundsException if pos is negative
    */
   public void set(int pos)
   {
-    if (pos < 0)
-      throw new IndexOutOfBoundsException();
-    int bit = pos % 64;
-    int offset = pos / 64;
+    int offset = pos >>> 6;
     ensure(offset);
-    bits[offset] |= 1L << bit;
+    // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+    // so we'll just let that be our exception.
+    bits[offset] |= 1L << pos;
+  }
+
+  /**
+   * Sets the bit at the given index to the specified value. The size of
+   * this structure is automatically increased as necessary.
+   *
+   * @param index the position to set
+   * @param value the value to set it to
+   * @throws IndexOutOfBoundsException if index is negative
+   * @since 1.4
+   */
+  public void set(int index, boolean value)
+  {
+    if (value)
+      set(index);
+    else
+      clear(index);
+  }
+
+  /**
+   * Sets the bits between from (inclusive) and to (exclusive) to true.
+   *
+   * @param from the start range (inclusive)
+   * @param to the end range (exclusive)
+   * @throws IndexOutOfBoundsException if from &lt; 0 || from &gt; to
+   * @since 1.4
+   */
+  public void set(int from, int to)
+  {
+    if (from < 0 || from > to)
+      throw new IndexOutOfBoundsException();
+    if (from == to)
+      return;
+    int lo_offset = from >>> 6;
+    int hi_offset = to >>> 6;
+    ensure(hi_offset);
+    if (lo_offset == hi_offset)
+      {
+        bits[hi_offset] |= (-1L << from) & ((1L << to) - 1);
+        return;
+      }
+
+    bits[lo_offset] |= -1L << from;
+    bits[hi_offset] |= (1L << to) - 1;
+    for (int i = lo_offset + 1; i < hi_offset; i++)
+      bits[i] = -1;
+  }
+
+  /**
+   * Sets the bits between from (inclusive) and to (exclusive) to the
+   * specified value.
+   *
+   * @param from the start range (inclusive)
+   * @param to the end range (exclusive)
+   * @param value the value to set it to
+   * @throws IndexOutOfBoundsException if from &lt; 0 || from &gt; to
+   * @since 1.4
+   */
+  public void set(int from, int to, boolean value)
+  {
+    if (value)
+      set(from, to);
+    else
+      clear(from, to);
   }
 
   /**
    * Returns the number of bits actually used by this bit set.  Note
-   * that this method doesn't return the number of set bits.
-   * @returns the number of bits currently used.  
+   * that this method doesn't return the number of set bits, and that
+   * future requests for larger bits will make this automatically grow.
+   *
+   * @return the number of bits currently used.
    */
   public int size()
   {
@@ -324,32 +659,32 @@ public class BitSet implements Cloneable, Serializable
    * Returns the string representation of this bit set.  This
    * consists of a comma separated list of the integers in this set
    * surrounded by curly braces.  There is a space after each comma.
+   * A sample string is thus "{1, 3, 53}".
    * @return the string representation.
    */
   public String toString()
   {
-    String r = "{";
+    StringBuffer r = new StringBuffer("{");
     boolean first = true;
     for (int i = 0; i < bits.length; ++i)
       {
-       long bit = 1;
-       long word = bits[i];
-       if (word == 0)
-         continue;
-       for (int j = 0; j < 64; ++j)
-         {
-           if ((word & bit) != 0)
-             {
-               if (!first)
-                 r += ", ";
-               r += Integer.toString(64 * i + j);
-               first = false;
-             }
-           bit <<= 1;
-         }
+        long bit = 1;
+        long word = bits[i];
+        if (word == 0)
+          continue;
+        for (int j = 0; j < 64; ++j)
+          {
+            if ((word & bit) != 0)
+              {
+                if (! first)
+                  r.append(", ");
+                r.append(64 * i + j);
+                first = false;
+              }
+            bit <<= 1;
+          }
       }
-
-    return r += "}";
+    return r.append("}").toString();
   }
 
   /**
@@ -357,32 +692,30 @@ public class BitSet implements Cloneable, Serializable
    * given <code>set</code>.  This means it builds the symmetric
    * remainder of the two sets (the elements that are in one set,
    * but not in the other).  The result is stored into this bit set,
-   * which grows as necessary.  
-   * @param set the second bit set.
-   * @exception OutOfMemoryError if the current set can't grow.  
-   * @require set != null
+   * which grows as necessary.
+   *
+   * @param bs the second bit set
+   * @throws NullPointerException if bs is null
    */
   public void xor(BitSet bs)
   {
     ensure(bs.bits.length - 1);
-    int i;
-    for (i = 0; i < bs.bits.length; ++i)
+    for (int i = bs.bits.length - 1; i >= 0; i--)
       bits[i] ^= bs.bits[i];
   }
 
-  // Make sure the vector is big enough.
+  /**
+   * Make sure the vector is big enough.
+   *
+   * @param lastElt the size needed for the bits array
+   */
   private final void ensure(int lastElt)
   {
-    if (lastElt + 1 > bits.length)
+    if (lastElt >= bits.length)
       {
-       long[] nd = new long[lastElt + 1];
-       System.arraycopy(bits, 0, nd, 0, bits.length);
-       bits = nd;
+        long[] nd = new long[lastElt + 1];
+        System.arraycopy(bits, 0, nd, 0, bits.length);
+        bits = nd;
       }
   }
-
-  // The actual bits.
-  long[] bits;
-
-  private static final long serialVersionUID = 7997698588986878753L;
 }
index 8e77a8024760a57f59ab79c5f2c1bfd5226ae8eb..2f54502899896a88f7cf8b984f083d094f44f5ed 100644 (file)
@@ -25,10 +25,6 @@ This exception does not however invalidate any other reasons why the
 executable file might be covered by the GNU General Public License. */
 
 
-// TO DO:
-// ~ Serialization is very much broken. Blame Sun for not specifying it.
-// ~ The synchronized* and unmodifiable* methods don't have doc-comments.
-
 package java.util;
 
 import java.io.Serializable;
@@ -40,9 +36,54 @@ import java.io.Serializable;
  * are unaware of collections, a method to return a list which consists of
  * multiple copies of one element, and methods which "wrap" collections to give
  * them extra properties, such as thread-safety and unmodifiability.
+ * <p>
+ *
+ * All methods which take a collection throw a {@link NullPointerException} if
+ * that collection is null. Algorithms which can change a collection may, but
+ * are not required, to throw the {@link UnsupportedOperationException} that
+ * the underlying collection would throw during an attempt at modification.
+ * For example,
+ * <code>Collections.singleton("").addAll(Collections.EMPTY_SET)<code>
+ * does not throw a exception, even though addAll is an unsupported operation
+ * on a singleton; the reason for this is that addAll did not attempt to
+ * modify the set.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see Set
+ * @see List
+ * @see Map
+ * @see Arrays
+ * @since 1.2
+ * @status updated to 1.4
  */
 public class Collections
 {
+  /**
+   * Constant used to decide cutoff for when a non-RandomAccess list should
+   * be treated as sequential-access. Basically, quadratic behavior is
+   * acceptible for small lists when the overhead is so small in the first
+   * place. I arbitrarily set it to 16, so it may need some tuning.
+   */
+  private static final int LARGE_LIST_SIZE = 16;
+
+  /**
+   * Determines if a list should be treated as a sequential-access one.
+   * Rather than the old method of JDK 1.3 of assuming only instanceof
+   * AbstractSequentialList should be sequential, this uses the new method
+   * of JDK 1.4 of assuming anything that does NOT implement RandomAccess
+   * and exceeds a large (unspecified) size should be sequential.
+   *
+   * @param l the list to check
+   * @return true if it should be treated as sequential-access
+   */
+  private static boolean isSequential(List l)
+  {
+    return ! (l instanceof RandomAccess) && l.size() > LARGE_LIST_SIZE;
+  }
+
   /**
    * This class is non-instantiable.
    */
@@ -51,200 +92,288 @@ public class Collections
   }
 
   /**
-   * An immutable, empty Set.
-   * Note: This implementation isn't Serializable, although it should be by the
-   * spec.
+   * An immutable, serializable, empty Set.
+   * @see Serializable
+   */
+  public static final Set EMPTY_SET = new EmptySet();
+
+  private static final Iterator EMPTY_ITERATOR = new Iterator()
+    {
+      public boolean hasNext()
+      {
+       return false;
+      }
+
+      public Object next()
+      {
+       throw new NoSuchElementException();
+      }
+
+      public void remove()
+      {
+       throw new UnsupportedOperationException();
+      }
+    };
+
+  /**
+   * The implementation of {@link #EMPTY_SET}. This class name is required
+   * for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
    */
-  public static final Set EMPTY_SET = new AbstractSet()
+  private static final class EmptySet extends AbstractSet
+    implements Serializable
   {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 1582296315990362920L;
+
+    /**
+     * A private constructor adds overhead.
+     */
+    EmptySet()
+    {
+    }
+
+    /**
+     * The size: always 0!
+     */
     public int size()
     {
       return 0;
     }
 
-    // This is really cheating! I think it's perfectly valid, though - the
-    // more conventional code is here, commented out, in case anyone disagrees.
+    /**
+     * Returns an iterator that does not iterate.
+     */
     public Iterator iterator()
     {
-      return EMPTY_LIST.iterator();
+      return EMPTY_ITERATOR;
     }
+  } // class EmptySet
 
-    // public Iterator iterator() {
-    //   return new Iterator() {
-    // 
-    //     public boolean hasNext() {
-    //       return false;
-    //     }
-    // 
-    //     public Object next() {
-    //       throw new NoSuchElementException();
-    //     }
-    // 
-    //     public void remove() {
-    //       throw new UnsupportedOperationException();
-    //     }
-    //   };
-    // }
-
-  };
+  /**
+   * An immutable, serializable, empty List, which implements RandomAccess.
+   * @see Serializable
+   * @see RandomAccess
+   */
+  public static final List EMPTY_LIST = new EmptyList();
 
   /**
-   * An immutable, empty List.
-   * Note: This implementation isn't serializable, although it should be by the
-   * spec.
+   * The implementation of {@link #EMPTY_LIST}. This class name is required
+   * for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
    */
-  public static final List EMPTY_LIST = new AbstractList()
+  private static final class EmptyList extends AbstractList
+    implements Serializable, RandomAccess
   {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 8842843931221139166L;
+
+    /**
+     * A private constructor adds overhead.
+     */
+    EmptyList()
+    {
+    }
+
+    /**
+     * The size is always 0.
+     */
     public int size()
     {
       return 0;
     }
 
+    /**
+     * No matter the index, it is out of bounds.
+     */
     public Object get(int index)
     {
       throw new IndexOutOfBoundsException();
     }
-  };
+    
+    /**
+     * Returns an iterator that does not iterate. Optional, but avoids 
+     * allocation of an iterator in AbstractList.
+     */
+    public Iterator iterator()
+    {
+      return EMPTY_ITERATOR;
+    }    
+  } // class EmptyList
+
+  /**
+   * An immutable, serializable, empty Map.
+   * @see Serializable
+   */
+  public static final Map EMPTY_MAP = new EmptyMap();
 
   /**
-   * An immutable, empty Map.
-   * Note: This implementation isn't serializable, although it should be by the
-   * spec.
+   * The implementation of {@link #EMPTY_MAP}. This class name is required
+   * for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
    */
-  public static final Map EMPTY_MAP = new AbstractMap()
+  private static final class EmptyMap extends AbstractMap
+    implements Serializable
   {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 6428348081105594320L;
+
+    /**
+     * A private constructor adds overhead.
+     */
+    EmptyMap()
+    {
+    }
+
+    /**
+     * There are no entries.
+     */
     public Set entrySet()
     {
       return EMPTY_SET;
     }
-  };
+
+    /**
+     * Size is always 0.
+     */
+    public int size()
+    {
+      return 0;
+    }
+
+    /**
+     * No entries. Technically, EMPTY_SET, while more specific than a general
+     * Collection, will work. Besides, that's what the JDK uses!
+     */
+    public Collection values()
+    {
+      return EMPTY_SET;
+    }
+  } // class EmptyMap
 
   /**
    * Compare two objects with or without a Comparator. If c is null, uses the
    * natural ordering. Slightly slower than doing it inline if the JVM isn't
    * clever, but worth it for removing a duplicate of the search code.
-   * Note: This same code is used in Arrays (for sort as well as search)
-   */
-  private static int compare(Object o1, Object o2, Comparator c)
-  {
-    if (c == null)
-      {
-       return ((Comparable) o1).compareTo(o2);
-      }
-    else
-      {
-       return c.compare(o1, o2);
-      }
-  }
-
-  /**
-   * The hard work for the search routines. If the Comparator given is null,
-   * uses the natural ordering of the elements.
+   * Note: This code is also used in Arrays (for sort as well as search).
    */
-  private static int search(List l, Object key, final Comparator c)
+  static final int compare(Object o1, Object o2, Comparator c)
   {
-    int pos = 0;
-
-    // We use a linear search using an iterator if we can guess that the list
-    // is sequential-access.
-    if (l instanceof AbstractSequentialList)
-      {
-       ListIterator itr = l.listIterator();
-       for (int i = l.size() - 1; i >= 0; --i)
-         {
-           final int d = compare(key, itr.next(), c);
-           if (d == 0)
-             {
-               return pos;
-             }
-           else if (d < 0)
-             {
-               return -pos - 1;
-             }
-           pos++;
-         }
-
-       // We assume the list is random-access, and use a binary search
-      }
-    else
-      {
-       int low = 0;
-       int hi = l.size() - 1;
-       while (low <= hi)
-         {
-           pos = (low + hi) >> 1;
-           final int d = compare(key, l.get(pos), c);
-           if (d == 0)
-             {
-               return pos;
-             }
-           else if (d < 0)
-             {
-               hi = pos - 1;
-             }
-           else
-             {
-               low = ++pos;    // This gets the insertion point right on the last loop
-             }
-         }
-      }
-
-    // If we failed to find it, we do the same whichever search we did.
-    return -pos - 1;
+    return c == null ? ((Comparable) o1).compareTo(o2) : c.compare(o1, o2);
   }
 
   /**
    * Perform a binary search of a List for a key, using the natural ordering of
    * the elements. The list must be sorted (as by the sort() method) - if it is
-   * not, the behaviour of this method is undefined, and may be an infinite
+   * not, the behavior of this method is undefined, and may be an infinite
    * loop. Further, the key must be comparable with every item in the list. If
-   * the list contains the key more than once, any one of them may be found. To
-   * avoid pathological behaviour on sequential-access lists, a linear search
-   * is used if (l instanceof AbstractSequentialList). Note: although the
+   * the list contains the key more than once, any one of them may be found.
+   * <p>
+   *
+   * This algorithm behaves in log(n) time for {@link RandomAccess} lists,
+   * and uses a linear search with O(n) link traversals and log(n) comparisons
+   * with {@link AbstractSequentialList} lists. Note: although the
    * specification allows for an infinite loop if the list is unsorted, it will
    * not happen in this (Classpath) implementation.
    *
    * @param l the list to search (must be sorted)
    * @param key the value to search for
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
-   * @exception ClassCastException if key could not be compared with one of the
-   *   elements of l
-   * @exception NullPointerException if a null element has compareTo called
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value
+   * @throws ClassCastException if key could not be compared with one of the
+   *         elements of l
+   * @throws NullPointerException if a null element has compareTo called
+   * @see #sort(List)
    */
   public static int binarySearch(List l, Object key)
   {
-    return search(l, key, null);
+    return binarySearch(l, key, null);
   }
 
   /**
    * Perform a binary search of a List for a key, using a supplied Comparator.
    * The list must be sorted (as by the sort() method with the same Comparator)
-   * - if it is not, the behaviour of this method is undefined, and may be an
+   * - if it is not, the behavior of this method is undefined, and may be an
    * infinite loop. Further, the key must be comparable with every item in the
    * list. If the list contains the key more than once, any one of them may be
-   * found. To avoid pathological behaviour on sequential-access lists, a
-   * linear search is used if (l instanceof AbstractSequentialList). Note:
-   * although the specification allows for an infinite loop if the list is
-   * unsorted, it will not happen in this (Classpath) implementation.
+   * found. If the comparator is null, the elements' natural ordering is used.
+   * <p>
+   *
+   * This algorithm behaves in log(n) time for {@link RandomAccess} lists,
+   * and uses a linear search with O(n) link traversals and log(n) comparisons
+   * with {@link AbstractSequentialList} lists. Note: although the
+   * specification allows for an infinite loop if the list is unsorted, it will
+   * not happen in this (Classpath) implementation.
    *
    * @param l the list to search (must be sorted)
    * @param key the value to search for
    * @param c the comparator by which the list is sorted
-   * @returns the index at which the key was found, or -n-1 if it was not
-   *   found, where n is the index of the first value higher than key or
-   *   a.length if there is no such value.
-   * @exception ClassCastException if key could not be compared with one of the
-   *   elements of l
+   * @return the index at which the key was found, or -n-1 if it was not
+   *         found, where n is the index of the first value higher than key or
+   *         a.length if there is no such value
+   * @throws ClassCastException if key could not be compared with one of the
+   *         elements of l
+   * @throws NullPointerException if a null element is compared with natural
+   *         ordering (only possible when c is null)
+   * @see #sort(List, Comparator)
    */
   public static int binarySearch(List l, Object key, Comparator c)
   {
-    if (c == null)
+    int pos = 0;
+    int low = 0;
+    int hi = l.size() - 1;
+
+    // We use a linear search with log(n) comparisons using an iterator
+    // if the list is sequential-access.
+    if (isSequential(l))
+      {
+       ListIterator itr = l.listIterator();
+        int i = 0;
+        while (low <= hi)
+          {
+            pos = (low + hi) >> 1;
+            if (i < pos)
+              for ( ; i != pos; i++, itr.next());
+            else
+              for ( ; i != pos; i--, itr.previous());
+           final int d = compare(key, itr.next(), c);
+           if (d == 0)
+              return pos;
+           else if (d < 0)
+              hi = pos - 1;
+           else
+              // This gets the insertion point right on the last loop
+              low = ++pos;
+          }
+      }
+    else
       {
-       throw new NullPointerException();
+       while (low <= hi)
+         {
+           pos = (low + hi) >> 1;
+           final int d = compare(key, l.get(pos), c);
+           if (d == 0)
+              return pos;
+           else if (d < 0)
+              hi = pos - 1;
+           else
+              // This gets the insertion point right on the last loop
+              low = ++pos;
+         }
       }
-    return search(l, key, c);
+
+    // If we failed to find it, we do the same whichever search we did.
+    return -pos - 1;
   }
 
   /**
@@ -252,30 +381,26 @@ public class Collections
    * source list, the remaining elements are unaffected. This method runs in
    * linear time.
    *
-   * @param dest the destination list.
-   * @param source the source list.
-   * @exception IndexOutOfBoundsException if the destination list is shorter
-   *   than the source list (the elements that can be copied will be, prior to
-   *   the exception being thrown).
-   * @exception UnsupportedOperationException if dest.listIterator() does not
-   *   support the set operation.
+   * @param dest the destination list
+   * @param source the source list
+   * @throws IndexOutOfBoundsException if the destination list is shorter
+   *         than the source list (the destination will be unmodified)
+   * @throws UnsupportedOperationException if dest.listIterator() does not
+   *         support the set operation
    */
   public static void copy(List dest, List source)
   {
+    int pos = source.size();
+    if (dest.size() < pos)
+      throw new IndexOutOfBoundsException("Source does not fit in dest");
+
     Iterator i1 = source.iterator();
     ListIterator i2 = dest.listIterator();
 
-    try
+    while (--pos >= 0)
       {
-       for (int i = source.size() - 1; i >= 0; --i)
-         {
-           i2.next();
-           i2.set(i1.next());
-         }
-      }
-    catch (NoSuchElementException x)
-      {
-       throw new IndexOutOfBoundsException("Source doesn't fit in dest.");      
+        i2.next();
+        i2.set(i1.next());
       }
   }
 
@@ -284,7 +409,7 @@ public class Collections
    * with legacy APIs that require an Enumeration as input.
    *
    * @param c the Collection to iterate over
-   * @returns an Enumeration backed by an Iterator over c
+   * @return an Enumeration backed by an Iterator over c
    */
   public static Enumeration enumeration(Collection c)
   {
@@ -308,8 +433,8 @@ public class Collections
    *
    * @param l the list to fill.
    * @param val the object to vill the list with.
-   * @exception UnsupportedOperationException if l.listIterator() does not
-   *   support the set operation.
+   * @throws UnsupportedOperationException if l.listIterator() does not
+   *         support the set operation.
    */
   public static void fill(List l, Object val)
   {
@@ -321,31 +446,82 @@ public class Collections
       }
   }
 
+  /**
+   * Returns the starting index where the specified sublist first occurs
+   * in a larger list, or -1 if there is no matching position. If
+   * <code>target.size() &gt; source.size()</code>, this returns -1,
+   * otherwise this implementation uses brute force, checking for
+   * <code>source.sublist(i, i + target.size()).equals(target)</code>
+   * for all possible i.
+   *
+   * @param source the list to search
+   * @param target the sublist to search for
+   * @return the index where found, or -1
+   * @since 1.4
+   */
+  public static int indexOfSubList(List source, List target)
+  {
+    int ssize = source.size();
+    for (int i = 0, j = target.size(); j <= ssize; i++, j++)
+      if (source.subList(i, j).equals(target))
+        return i;
+    return -1;
+  }
+
+  /**
+   * Returns the starting index where the specified sublist last occurs
+   * in a larger list, or -1 if there is no matching position. If
+   * <code>target.size() &gt; source.size()</code>, this returns -1,
+   * otherwise this implementation uses brute force, checking for
+   * <code>source.sublist(i, i + target.size()).equals(target)</code>
+   * for all possible i.
+   *
+   * @param source the list to search
+   * @param target the sublist to search for
+   * @return the index where found, or -1
+   * @since 1.4
+   */
+  public static int lastIndexOfSubList(List source, List target)
+  {
+    int ssize = source.size();
+    for (int i = ssize - target.size(), j = ssize; i >= 0; i--, j--)
+      if (source.subList(i, j).equals(target))
+        return i;
+    return -1;
+  }
+
+  /**
+   * Returns an array list holding the elements visited by a given
+   * Enumeration. This method exists for interoperability between legacy
+   * APIs and the new Collection API.
+   *
+   * @param e the enumeration to put in a list
+   * @return a list containing the enumeration elements
+   * @see ArrayList
+   * @since 1.4
+   */
+  public static List list(Enumeration e)
+  {
+    List l = new ArrayList();
+    while (e.hasMoreElements())
+      l.add(e.nextElement());
+    return l;
+  }
+
   /**
    * Find the maximum element in a Collection, according to the natural
    * ordering of the elements. This implementation iterates over the
    * Collection, so it works in linear time.
    *
    * @param c the Collection to find the maximum element of
-   * @returns the maximum element of c
+   * @return the maximum element of c
    * @exception NoSuchElementException if c is empty
    * @exception ClassCastException if elements in c are not mutually comparable
    * @exception NullPointerException if null.compareTo is called
    */
   public static Object max(Collection c)
   {
-    Iterator itr = c.iterator();
-    Comparable max = (Comparable) itr.next();  // throws NoSuchElementException
-    int csize = c.size();
-    for (int i = 1; i < csize; i++)
-      {
-       Object o = itr.next();
-       if (max.compareTo(o) < 0)
-         {
-           max = (Comparable) o;
-         }
-      }
-    return max;
+    return max(c, null);
   }
 
   /**
@@ -354,20 +530,23 @@ public class Collections
    * works in linear time.
    *
    * @param c the Collection to find the maximum element of
-   * @param order the Comparator to order the elements by
-   * @returns the maximum element of c
-   * @exception NoSuchElementException if c is empty
-   * @exception ClassCastException if elements in c are not mutually comparable
+   * @param order the Comparator to order the elements by, or null for natural
+   *        ordering
+   * @return the maximum element of c
+   * @throws NoSuchElementException if c is empty
+   * @throws ClassCastException if elements in c are not mutually comparable
+   * @throws NullPointerException if null is compared by natural ordering
+   *        (only possible when order is null)
    */
   public static Object max(Collection c, Comparator order)
   {
     Iterator itr = c.iterator();
-    Object max = itr.next();   // throws NoSuchElementException
+    Object max = itr.next(); // throws NoSuchElementException
     int csize = c.size();
     for (int i = 1; i < csize; i++)
       {
        Object o = itr.next();
-       if (order.compare(max, o) < 0)
+       if (compare(max, o, order) < 0)
          max = o;
       }
     return max;
@@ -379,23 +558,14 @@ public class Collections
    * Collection, so it works in linear time.
    *
    * @param c the Collection to find the minimum element of
-   * @returns the minimum element of c
-   * @exception NoSuchElementException if c is empty
-   * @exception ClassCastException if elements in c are not mutually comparable
-   * @exception NullPointerException if null.compareTo is called
+   * @return the minimum element of c
+   * @throws NoSuchElementException if c is empty
+   * @throws ClassCastException if elements in c are not mutually comparable
+   * @throws NullPointerException if null.compareTo is called
    */
   public static Object min(Collection c)
   {
-    Iterator itr = c.iterator();
-    Comparable min = (Comparable) itr.next();  // throws NoSuchElementException
-    int csize = c.size();
-    for (int i = 1; i < csize; i++)
-      {
-       Object o = itr.next();
-       if (min.compareTo(o) > 0)
-         min = (Comparable) o;
-      }
-    return min;
+    return min(c, null);
   }
 
   /**
@@ -404,10 +574,13 @@ public class Collections
    * works in linear time.
    *
    * @param c the Collection to find the minimum element of
-   * @param order the Comparator to order the elements by
-   * @returns the minimum element of c
-   * @exception NoSuchElementException if c is empty
-   * @exception ClassCastException if elements in c are not mutually comparable
+   * @param order the Comparator to order the elements by, or null for natural
+   *        ordering
+   * @return the minimum element of c
+   * @throws NoSuchElementException if c is empty
+   * @throws ClassCastException if elements in c are not mutually comparable
+   * @throws NullPointerException if null is compared by natural ordering
+   *        (only possible when order is null)
    */
   public static Object min(Collection c, Comparator order)
   {
@@ -417,7 +590,7 @@ public class Collections
     for (int i = 1; i < csize; i++)
       {
        Object o = itr.next();
-       if (order.compare(min, o) > 0)
+       if (compare(min, o, order) > 0)
          min = o;
       }
     return min;
@@ -426,54 +599,182 @@ public class Collections
   /**
    * Creates an immutable list consisting of the same object repeated n times.
    * The returned object is tiny, consisting of only a single reference to the
-   * object and a count of the number of elements. It is Serializable.
+   * object and a count of the number of elements. It is Serializable, and
+   * implements RandomAccess. You can use it in tandem with List.addAll for
+   * fast list construction.
    *
    * @param n the number of times to repeat the object
    * @param o the object to repeat
-   * @returns a List consisting of n copies of o
-   * @throws IllegalArgumentException if n < 0
+   * @return a List consisting of n copies of o
+   * @throws IllegalArgumentException if n &lt; 0
+   * @see List#addAll(Collection)
+   * @see Serializable
+   * @see RandomAccess
    */
-  // It's not Serializable, because the serialized form is unspecced.
-  // Also I'm only assuming that it should be because I don't think it's
-  // stated - I just would be amazed if it isn't...
   public static List nCopies(final int n, final Object o)
   {
-    // Check for insane arguments
-    if (n < 0)
-      {
+    return new CopiesList(n, o);
+  }
+
+  /**
+   * The implementation of {@link #nCopies(int, Object)}. This class name
+   * is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class CopiesList extends AbstractList
+    implements Serializable, RandomAccess
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 2739099268398711800L;
+
+    /**
+     * The count of elements in this list.
+     * @serial the list size
+     */
+    private final int n;
+
+    /**
+     * The repeated list element.
+     * @serial the list contents
+     */
+    private final Object element;
+
+    /**
+     * Constructs the list.
+     *
+     * @param n the count
+     * @param o the object
+     * @throws IllegalArgumentException if n &lt; 0
+     */
+    CopiesList(int n, Object o)
+    {
+      if (n < 0)
        throw new IllegalArgumentException();
-      }
+      this.n = n;
+      element = o;
+    }
 
-    // Create a minimal implementation of List
-    return new AbstractList()
+    /**
+     * The size is fixed.
+     */
+    public int size()
     {
-      public int size()
-      {
-       return n;
-      }
+      return n;
+    }
 
-      public Object get(int index)
-      {
-       if (index < 0 || index >= n)
-         {
-           throw new IndexOutOfBoundsException();
-         }
-       return o;
-      }
-    };
+    /**
+     * The same element is returned.
+     */
+    public Object get(int index)
+    {
+      if (index < 0 || index >= n)
+        throw new IndexOutOfBoundsException();
+      return element;
+    }
+
+    // The remaining methods are optional, but provide a performance
+    // advantage by not allocating unnecessary iterators in AbstractList.
+    /**
+     * This list only contains one element.
+     */
+    public boolean contains(Object o)
+    {
+      return n > 0 && equals(o, element);
+    }
+
+    /**
+     * The index is either 0 or -1.
+     */
+    public int indexOf(Object o)
+    {
+      return (n > 0 && equals(o, element)) ? 0 : -1;
+    }
+
+    /**
+     * The index is either n-1 or -1.
+     */
+    public int lastIndexOf(Object o)
+    {
+      return equals(o, element) ? n - 1 : -1;
+    }
+
+    /**
+     * A subList is just another CopiesList.
+     */
+    public List subList(int from, int to)
+    {
+      if (from < 0 || to > n)
+        throw new IndexOutOfBoundsException();
+      return new CopiesList(to - from, element);
+    }
+
+    /**
+     * The array is easy.
+     */
+    public Object[] toArray()
+    {
+      Object[] a = new Object[n];
+      Arrays.fill(a, element);
+      return a;
+    }
+
+    /**
+     * The string is easy to generate.
+     */
+    public String toString()
+    {
+      StringBuffer r = new StringBuffer("{");
+      for (int i = n - 1; --i > 0; )
+        r.append(element).append(", ");
+      r.append(element).append("}");
+      return r.toString();
+    }
+  } // class CopiesList
+
+  /**
+   * Replace all instances of one object with another in the specified list.
+   * The list does not change size. An element e is replaced if
+   * <code>oldval == null ? e == null : oldval.equals(e)</code>.
+   *
+   * @param list the list to iterate over
+   * @param oldval the element to replace
+   * @param newval the new value for the element
+   * @return true if a replacement occurred
+   * @throws UnsupportedOperationException if the list iterator does not allow
+   *         for the set operation
+   * @throws ClassCastException newval is of a type which cannot be added
+   *         to the list
+   * @throws IllegalArgumentException some other aspect of newval stops
+   *         it being added to the list
+   * @since 1.4
+   */
+  public static boolean replaceAll(List list, Object oldval, Object newval)
+  {
+    ListIterator itr = list.listIterator();
+    boolean replace_occured = false;
+    for (int i = list.size(); --i >= 0; )
+      if (AbstractCollection.equals(oldval, itr.next()))
+        {
+          itr.set(newval);
+          replace_occured = true;
+        }
+    return replace_occured;
   }
 
   /**
    * Reverse a given list. This method works in linear time.
    *
-   * @param l the list to reverse.
-   * @exception UnsupportedOperationException if l.listIterator() does not
-   *   support the set operation.
+   * @param l the list to reverse
+   * @throws UnsupportedOperationException if l.listIterator() does not
+   *         support the set operation
    */
   public static void reverse(List l)
   {
     ListIterator i1 = l.listIterator();
-    int pos1 = 0;
+    int pos1 = 1;
     int pos2 = l.size();
     ListIterator i2 = l.listIterator(pos2);
     while (pos1 < pos2)
@@ -486,42 +787,152 @@ public class Collections
       }
   }
 
-  static class ReverseComparator implements Comparator, Serializable
+  /**
+   * Get a comparator that implements the reverse of natural ordering. In
+   * other words, this sorts Comparable objects opposite of how their
+   * compareTo method would sort. This makes it easy to sort into reverse
+   * order, by simply passing Collections.reverseOrder() to the sort method.
+   * The return value of this method is Serializable.
+   *
+   * @return a comparator that imposes reverse natural ordering
+   * @see Comparable
+   * @see Serializable
+   */
+  public static Comparator reverseOrder()
+  {
+    return rcInstance;
+  }
+
+  /**
+   * The object for {@link #reverseOrder()}.
+   */
+  static private final ReverseComparator rcInstance = new ReverseComparator();
+
+  /**
+   * The implementation of {@link #reverseOrder()}. This class name
+   * is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class ReverseComparator
+    implements Comparator, Serializable
   {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    static private final long serialVersionUID = 7207038068494060240L;
+
+    /**
+     * A private constructor adds overhead.
+     */
+    ReverseComparator()
+    {
+    }
+
+    /**
+     * Compare two objects in reverse natural order.
+     *
+     * @param a the first object
+     * @param b the second object
+     * @return &lt;, ==, or &gt; 0 according to b.compareTo(a)
+     */
     public int compare(Object a, Object b)
     {
-      return -((Comparable) a).compareTo(b);
+      return ((Comparable) b).compareTo(a);
     }
   }
-  
-  static ReverseComparator rcInstance = new ReverseComparator();
-  
+
   /**
-   * Get a comparator that implements the reverse of natural ordering. This is
-   * intended to make it easy to sort into reverse order, by simply passing
-   * Collections.reverseOrder() to the sort method. The return value of this
-   * method is Serializable.
+   * Rotate the elements in a list by a specified distance. After calling this
+   * method, the element now at index <code>i</code> was formerly at index
+   * <code>(i - distance) mod list.size()</code>. The list size is unchanged.
+   * <p>
+   *
+   * For example, suppose a list contains <code>[t, a, n, k, s]</code>. After
+   * either <code>Collections.rotate(l, 4)</code> or
+   * <code>Collections.rotate(l, -1)</code>, the new contents are
+   * <code>[s, t, a, n, k]</code>. This can be applied to sublists to rotate
+   * just a portion of the list. For example, to move element <code>a</code>
+   * forward two positions in the original example, use
+   * <code>Collections.rotate(l.subList(1, 3+1), -1)</code>, which will
+   * result in <code>[t, n, k, a, s]</code>.
+   * <p>
+   *
+   * If the list is small or implements {@link RandomAccess}, the
+   * implementation exchanges the first element to its destination, then the
+   * displaced element, and so on until a circuit has been completed. The
+   * process is repeated if needed on the second element, and so forth, until
+   * all elements have been swapped.  For large non-random lists, the
+   * implementation breaks the list into two sublists at index
+   * <code>-distance mod size</code>, calls {@link #reverse(List)} on the
+   * pieces, then reverses the overall list.
+   *
+   * @param list the list to rotate
+   * @param distance the distance to rotate by; unrestricted in value
+   * @throws UnsupportedOperationException if the list does not support set
+   * @since 1.4
    */
-  public static Comparator reverseOrder()
+  public static void rotate(List list, int distance)
   {
-    return rcInstance;
+    int size = list.size();
+    distance %= size;
+    if (distance == 0)
+      return;
+    if (distance < 0)
+      distance += size;
+
+    if (isSequential(list))
+      {
+        reverse(list);
+        reverse(list.subList(0, distance));
+        reverse(list.subList(distance, size));
+      }
+    else
+      {
+        // Determine the least common multiple of distance and size, as there
+        // are (distance / LCM) loops to cycle through.
+        int a = size;
+        int lcm = distance;
+        int b = a % lcm;
+        while (b != 0)
+          {
+            a = lcm;
+            lcm = b;
+            b = a % lcm;
+          }
+
+        // Now, make the swaps. We must take the remainder every time through
+        // the inner loop so that we don't overflow i to negative values.
+        while (--lcm >= 0)
+          {
+            Object o = list.get(lcm);
+            for (int i = lcm + distance; i != lcm; i = (i + distance) % size)
+              o = list.set(i, o);
+            list.set(lcm, o);
+          }
+      }
   }
 
   /**
    * Shuffle a list according to a default source of randomness. The algorithm
-   * used would result in a perfectly fair shuffle (that is, each element would
-   * have an equal chance of ending up in any position) with a perfect source
-   * of randomness; in practice the results are merely very close to perfect.
+   * used iterates backwards over the list, swapping each element with an
+   * element randomly selected from the elements in positions less than or
+   * equal to it (using r.nextInt(int)).
+   * <p>
+   *
+   * This algorithm would result in a perfectly fair shuffle (that is, each
+   * element would have an equal chance of ending up in any position) if r were
+   * a perfect source of randomness. In practice the results are merely very
+   * close to perfect.
    * <p>
-   * This method operates in linear time on a random-access list, but may take
-   * quadratic time on a sequential-access list.
-   * Note: this (classpath) implementation will never take quadratic time, but
-   * it does make a copy of the list. This is in line with the behaviour of the
-   * sort methods and seems preferable.
    *
-   * @param l the list to shuffle.
-   * @exception UnsupportedOperationException if l.listIterator() does not
-   *   support the set operation.
+   * This method operates in linear time. To do this on large lists which do
+   * not implement {@link RandomAccess}, a temporary array is used to acheive
+   * this speed, since it would be quadratic access otherwise.
+   *
+   * @param l the list to shuffle
+   * @throws UnsupportedOperationException if l.listIterator() does not
+   *         support the set operation
    */
   public static void shuffle(List l)
   {
@@ -536,12 +947,13 @@ public class Collections
     shuffle(l, defaultRandom);
   }
 
-  /** Cache a single Random object for use by shuffle(List). This improves
-    * performance as well as ensuring that sequential calls to shuffle() will
-    * not result in the same shuffle order occurring: the resolution of 
-    * System.currentTimeMillis() is not sufficient to guarantee a unique seed.
-    */
-  private static Random defaultRandom = null;
+  /**
+   * Cache a single Random object for use by shuffle(List). This improves
+   * performance as well as ensuring that sequential calls to shuffle() will
+   * not result in the same shuffle order occurring: the resolution of
+   * System.currentTimeMillis() is not sufficient to guarantee a unique seed.
+   */
+  private static Random defaultRandom = null;
 
   /**
    * Shuffle a list according to a given source of randomness. The algorithm
@@ -549,148 +961,469 @@ public class Collections
    * element randomly selected from the elements in positions less than or
    * equal to it (using r.nextInt(int)).
    * <p>
+   *
    * This algorithm would result in a perfectly fair shuffle (that is, each
    * element would have an equal chance of ending up in any position) if r were
    * a perfect source of randomness. In practise (eg if r = new Random()) the
    * results are merely very close to perfect.
    * <p>
-   * This method operates in linear time on a random-access list, but may take
-   * quadratic time on a sequential-access list.
-   * Note: this (classpath) implementation will never take quadratic time, but
-   * it does make a copy of the list. This is in line with the behaviour of the
-   * sort methods and seems preferable.
    *
-   * @param l the list to shuffle.
-   * @param r the source of randomness to use for the shuffle.
-   * @exception UnsupportedOperationException if l.listIterator() does not
-   *   support the set operation.
+   * This method operates in linear time. To do this on large lists which do
+   * not implement {@link RandomAccess}, a temporary array is used to acheive
+   * this speed, since it would be quadratic access otherwise.
+   *
+   * @param l the list to shuffle
+   * @param r the source of randomness to use for the shuffle
+   * @throws UnsupportedOperationException if l.listIterator() does not
+   *         support the set operation
    */
   public static void shuffle(List l, Random r)
   {
-    Object[] a = l.toArray();  // Dump l into an array
     int lsize = l.size();
     ListIterator i = l.listIterator(lsize);
+    boolean sequential = isSequential(l);
+    Object[] a = null; // stores a copy of the list for the sequential case
+
+    if (sequential)
+      a = l.toArray();
 
-    // Iterate backwards over l
-    for (int pos = lsize - 1; pos >= 0; --pos)
+    for (int pos = lsize - 1; pos > 0; --pos)
       {
        // Obtain a random position to swap with. pos + 1 is used so that the
        // range of the random number includes the current position.
        int swap = r.nextInt(pos + 1);
 
-       // Swap the swapth element of the array with the next element of the
-       // list.
-       Object o = a[swap];
-       a[swap] = a[pos];
-       a[pos] = o;
+       // Swap the desired element.
+       Object o;
+        if (sequential)
+          {
+            o = a[swap];
+            a[swap] = i.previous();
+          }
+        else
+          o = l.set(swap, i.previous());
 
-       // Set the element in the original list accordingly.
-       i.previous();
        i.set(o);
       }
   }
 
+\f
   /**
    * Obtain an immutable Set consisting of a single element. The return value
    * of this method is Serializable.
    *
-   * @param o the single element.
-   * @return an immutable Set containing only o.
+   * @param o the single element
+   * @return an immutable Set containing only o
+   * @see Serializable
+   */
+  public static Set singleton(Object o)
+  {
+    return new SingletonSet(o);
+  }
+
+  /**
+   * The implementation of {@link #singleton(Object)}. This class name
+   * is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
    */
-  // It's not serializable because the spec is broken.
-  public static Set singleton(final Object o)
+  private static final class SingletonSet extends AbstractSet
+    implements Serializable
   {
-    return new AbstractSet()
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 3193687207550431679L;
+
+
+    /**
+     * The single element; package visible for use in nested class.
+     * @serial the singleton
+     */
+    final Object element;
+
+    /**
+     * Construct a singleton.
+     * @param o the element
+     */
+    SingletonSet(Object o)
     {
-      public int size()
-      {
-       return 1;
-      }
+      element = o;
+    }
 
-      public Iterator iterator()
-      {
-       return new Iterator()
-       {
-         private boolean hasNext = true;
+    /**
+     * The size: always 1!
+     */
+    public int size()
+    {
+      return 1;
+    }
 
-         public boolean hasNext()
-         {
-           return hasNext;
-         }
+    /**
+     * Returns an iterator over the lone element.
+     */
+    public Iterator iterator()
+    {
+      return new Iterator()
+      {
+        private boolean hasNext = true;
+
+        public boolean hasNext()
+        {
+          return hasNext;
+        }
+
+        public Object next()
+        {
+          if (hasNext)
+          {
+            hasNext = false;
+            return element;
+          }
+          else
+            throw new NoSuchElementException();
+        }
+
+        public void remove()
+        {
+          throw new UnsupportedOperationException();
+        }
+      };
+    }
 
-         public Object next()
-         {
-           if (hasNext)
-             {
-               hasNext = false;
-               return o;
-             }
-           else
-             {
-               throw new NoSuchElementException();
-             }
-         }
+    // The remaining methods are optional, but provide a performance
+    // advantage by not allocating unnecessary iterators in AbstractSet.
+    /**
+     * The set only contains one element.
+     */
+    public boolean contains(Object o)
+    {
+      return equals(o, element);
+    }
 
-         public void remove()
-         {
-           throw new UnsupportedOperationException();
-         }
-       };
-      }
-    };
-  }
+    /**
+     * This is true if the other collection only contains the element.
+     */
+    public boolean containsAll(Collection c)
+    {
+      Iterator i = c.iterator();
+      int pos = c.size();
+      while (--pos >= 0)
+        if (! equals(i.next(), element))
+          return false;
+      return true;
+    }
+
+    /**
+     * The hash is just that of the element.
+     */
+    public int hashCode()
+    {
+      return hashCode(element);
+    }
+
+    /**
+     * Returning an array is simple.
+     */
+    public Object[] toArray()
+    {
+      return new Object[] {element};
+    }
+
+    /**
+     * Obvious string.
+     */
+    public String toString()
+    {
+      return "[" + element + "]";
+    }
+  } // class SingletonSet
 
   /**
    * Obtain an immutable List consisting of a single element. The return value
-   * of this method is Serializable.
+   * of this method is Serializable, and implements RandomAccess.
+   *
+   * @param o the single element
+   * @return an immutable List containing only o
+   * @see Serializable
+   * @see RandomAccess
+   * @since 1.3
+   */
+  public static List singletonList(Object o)
+  {
+    return new SingletonList(o);
+  }
+
+  /**
+   * The implementation of {@link #singletonList(Object)}. This class name
+   * is required for compatibility with Sun's JDK serializability.
    *
-   * @param o the single element.
-   * @return an immutable List containing only o.
+   * @author Eric Blake <ebb9@email.byu.edu>
    */
-  // It's not serializable because the spec is broken.
-  public static List singletonList(final Object o)
+  private static final class SingletonList extends AbstractList
+    implements Serializable, RandomAccess
   {
-    return new AbstractList()
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 3093736618740652951L;
+
+    /**
+     * The single element.
+     * @serial the singleton
+     */
+    private final Object element;
+
+    /**
+     * Construct a singleton.
+     * @param o the element
+     */
+    SingletonList(Object o)
     {
-      public int size()
-      {
-       return 1;
-      }
+      element = o;
+    }
 
-      public Object get(int index)
-      {
-       if (index == 0)
-         {
-           throw new IndexOutOfBoundsException();
-         }
-       else
-         {
-           return o;
-         }
-      }
-    };
-  }
+    /**
+     * The size: always 1!
+     */
+    public int size()
+    {
+      return 1;
+    }
+
+    /**
+     * Only index 0 is valid.
+     */
+    public Object get(int index)
+    {
+      if (index == 0)
+        return element;
+      throw new IndexOutOfBoundsException();
+    }
+
+    // The remaining methods are optional, but provide a performance
+    // advantage by not allocating unnecessary iterators in AbstractList.
+    /**
+     * The set only contains one element.
+     */
+    public boolean contains(Object o)
+    {
+      return equals(o, element);
+    }
+
+    /**
+     * This is true if the other collection only contains the element.
+     */
+    public boolean containsAll(Collection c)
+    {
+      Iterator i = c.iterator();
+      int pos = c.size();
+      while (--pos >= 0)
+        if (! equals(i.next(), element))
+          return false;
+      return true;
+    }
+
+    /**
+     * Speed up the hashcode computation.
+     */
+    public int hashCode()
+    {
+      return 31 + hashCode(element);
+    }
+
+    /**
+     * Either the list has it or not.
+     */
+    public int indexOf(Object o)
+    {
+      return equals(o, element) ? 0 : -1;
+    }
+
+    /**
+     * Either the list has it or not.
+     */
+    public int lastIndexOf(Object o)
+    {
+      return equals(o, element) ? 0 : -1;
+    }
+
+    /**
+     * Sublists are limited in scope.
+     */
+    public List subList(int from, int to)
+    {
+      if (from == to && (to == 0 || to == 1))
+        return EMPTY_LIST;
+      if (from == 0 && to == 1)
+        return this;
+      if (from > to)
+        throw new IllegalArgumentException();
+      throw new IndexOutOfBoundsException();
+    }
+
+    /**
+     * Returning an array is simple.
+     */
+    public Object[] toArray()
+    {
+      return new Object[] {element};
+    }
+
+    /**
+     * Obvious string.
+     */
+    public String toString()
+    {
+      return "[" + element + "]";
+    }
+  } // class SingletonList
 
   /**
-   * Obtain an immutable Map consisting of a single key value pair.
+   * Obtain an immutable Map consisting of a single key-value pair.
    * The return value of this method is Serializable.
    *
-   * @param key the single key.
-   * @param value the single value.
-   * @return an immutable Map containing only the single key value pair.
+   * @param key the single key
+   * @param value the single value
+   * @return an immutable Map containing only the single key-value pair
+   * @see Serializable
+   * @since 1.3
    */
-  // It's not serializable because the spec is broken.
-  public static Map singletonMap(final Object key, final Object value)
+  public static Map singletonMap(Object key, Object value)
   {
-    return new AbstractMap()
-    {
-      public Set entrySet()
-      {
-       return singleton(new BasicMapEntry(key, value));
-      }
-    };
+    return new SingletonMap(key, value);
   }
 
+  /**
+   * The implementation of {@link #singletonMap(Object)}. This class name
+   * is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class SingletonMap extends AbstractMap
+    implements Serializable
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -6979724477215052911L;
+
+    /**
+     * The single key.
+     * @serial the singleton key
+     */
+    private final Object k;
+
+    /**
+     * The corresponding value.
+     * @serial the singleton value
+     */
+    private final Object v;
+
+    /**
+     * Cache the entry set.
+     */
+    private transient Set entries;
+
+    /**
+     * Construct a singleton.
+     * @param key the key
+     * @param value the value
+     */
+    SingletonMap(Object key, Object value)
+    {
+      k = key;
+      v = value;
+    }
+
+    /**
+     * There is a single immutable entry.
+     */
+    public Set entrySet()
+    {
+      if (entries == null)
+        entries = singleton(new BasicMapEntry(k, v)
+        {
+          public Object setValue(Object o)
+          {
+            throw new UnsupportedOperationException();
+          }
+        });
+      return entries;
+    }
+
+    // The remaining methods are optional, but provide a performance
+    // advantage by not allocating unnecessary iterators in AbstractMap.
+    /**
+     * Single entry.
+     */
+    public boolean containsKey(Object key)
+    {
+      return equals(key, k);
+    }
+
+    /**
+     * Single entry.
+     */
+    public boolean containsValue(Object value)
+    {
+      return equals(value, v);
+    }
+
+    /**
+     * Single entry.
+     */
+    public Object get(Object key)
+    {
+      return equals(key, k) ? v : null;
+    }
+
+    /**
+     * Calculate the hashcode directly.
+     */
+    public int hashCode()
+    {
+      return hashCode(k) ^ hashCode(v);
+    }
+
+    /**
+     * Return the keyset.
+     */
+    public Set keySet()
+    {
+      if (keys == null)
+        keys = singleton(k);
+      return keys;
+    }
+
+    /**
+     * The size: always 1!
+     */
+    public int size()
+    {
+      return 1;
+    }
+
+    /**
+     * Return the values. Technically, a singleton, while more specific than
+     * a general Collection, will work. Besides, that's what the JDK uses!
+     */
+    public Collection values()
+    {
+      if (values == null)
+        values = singleton(v);
+      return values;
+    }
+
+    /**
+     * Obvious string.
+     */
+    public String toString()
+    {
+      return "{" + k + "=" + v + "}";
+    }
+  } // class SingletonMap
+
   /**
    * Sort a list according to the natural ordering of its elements. The list
    * must be modifiable, but can be of fixed size. The sort algorithm is
@@ -700,19 +1433,14 @@ public class Collections
    * the array.
    *
    * @param l the List to sort
-   * @exception ClassCastException if some items are not mutually comparable
-   * @exception UnsupportedOperationException if the List is not modifiable
+   * @throws ClassCastException if some items are not mutually comparable
+   * @throws UnsupportedOperationException if the List is not modifiable
+   * @throws NullPointerException if some element is null
+   * @see Arrays#sort(Object[])
    */
   public static void sort(List l)
   {
-    Object[] a = l.toArray();
-    Arrays.sort(a);
-    ListIterator i = l.listIterator();
-    for (int pos = 0; pos < a.length; pos++)
-      {
-       i.next();
-       i.set(a[pos]);
-      }
+    sort(l, null);
   }
 
   /**
@@ -724,1102 +1452,1897 @@ public class Collections
    * the array.
    *
    * @param l the List to sort
-   * @param c the Comparator specifying the ordering for the elements
-   * @exception ClassCastException if c will not compare some pair of items
-   * @exception UnsupportedOperationException if the List is not modifiable
+   * @param c the Comparator specifying the ordering for the elements, or
+   *        null for natural ordering
+   * @throws ClassCastException if c will not compare some pair of items
+   * @throws UnsupportedOperationException if the List is not modifiable
+   * @throws NullPointerException if null is compared by natural ordering
+   *        (only possible when c is null)
+   * @see Arrays#sort(Object[], Comparator)
    */
   public static void sort(List l, Comparator c)
   {
     Object[] a = l.toArray();
     Arrays.sort(a, c);
-    ListIterator i = l.listIterator();
-    for (int pos = 0; pos < a.length; pos++)
+    ListIterator i = l.listIterator(a.length);
+    for (int pos = a.length; --pos >= 0; )
       {
-       i.next();
+       i.previous();
        i.set(a[pos]);
       }
   }
 
-  // All the methods from here on in require doc-comments.
+  /**
+   * Swaps the elements at the specified positions within the list. Equal
+   * positions have no effect.
+   *
+   * @param l the list to work on
+   * @param i the first index to swap
+   * @param j the second index
+   * @throws UnsupportedOperationException if list.set is not supported
+   * @throws IndexOutOfBoundsException if either i or j is &lt; 0 or &gt;=
+   *         list.size()
+   * @since 1.4
+   */
+  public static void swap(List l, int i, int j)
+  {
+    l.set(i, l.set(j, l.get(i)));
+  }
 
+  /**
+   * Returns a synchronized (thread-safe) collection wrapper backed by the
+   * given collection. Notice that element access through the iterators
+   * is thread-safe, but if the collection can be structurally modified
+   * (adding or removing elements) then you should synchronize around the
+   * iteration to avoid non-deterministic behavior:<br>
+   * <pre>
+   * Collection c = Collections.synchronizedCollection(new Collection(...));
+   * ...
+   * synchronized (c)
+   *   {
+   *     Iterator i = c.iterator();
+   *     while (i.hasNext())
+   *       foo(i.next());
+   *   }
+   * </pre><p>
+   *
+   * Since the collection might be a List or a Set, and those have incompatible
+   * equals and hashCode requirements, this relies on Object's implementation
+   * rather than passing those calls on to the wrapped collection. The returned
+   * Collection implements Serializable, but can only be serialized if
+   * the collection it wraps is likewise Serializable.
+   *
+   * @param c the collection to wrap
+   * @return a synchronized view of the collection
+   * @see Serializable
+   */
   public static Collection synchronizedCollection(Collection c)
   {
     return new SynchronizedCollection(c);
   }
-  public static List synchronizedList(List l)
-  {
-    return new SynchronizedList(l);
-  }
-  public static Map synchronizedMap(Map m)
-  {
-    return new SynchronizedMap(m);
-  }
-  public static Set synchronizedSet(Set s)
-  {
-    return new SynchronizedSet(s);
-  }
-  public static SortedMap synchronizedSortedMap(SortedMap m)
-  {
-    return new SynchronizedSortedMap(m);
-  }
-  public static SortedSet synchronizedSortedSet(SortedSet s)
-  {
-    return new SynchronizedSortedSet(s);
-  }
-  public static Collection unmodifiableCollection(Collection c)
-  {
-    return new UnmodifiableCollection(c);
-  }
-  public static List unmodifiableList(List l)
-  {
-    return new UnmodifiableList(l);
-  }
-  public static Map unmodifiableMap(Map m)
-  {
-    return new UnmodifiableMap(m);
-  }
-  public static Set unmodifiableSet(Set s)
-  {
-    return new UnmodifiableSet(s);
-  }
-  public static SortedMap unmodifiableSortedMap(SortedMap m)
-  {
-    return new UnmodifiableSortedMap(m);
-  }
-  public static SortedSet unmodifiableSortedSet(SortedSet s)
-  {
-    return new UnmodifiableSortedSet(s);
-  }
 
-  // Sun's spec will need to be checked for the precise names of these
-  // classes, for serializability's sake. However, from what I understand,
-  // serialization is broken for these classes anyway.
-
-  // Note: although this code is largely uncommented, it is all very
-  // mechanical and there's nothing really worth commenting.
-  // When serialization of these classes works, we'll need doc-comments on
-  // them to document the serialized form.
-
-  private static class UnmodifiableIterator implements Iterator
+  /**
+   * The implementation of {@link #synchronizedCollection(Collection)}. This
+   * class name is required for compatibility with Sun's JDK serializability.
+   * Package visible, so that collections such as the one for
+   * Hashtable.values() can specify which object to synchronize on.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  static class SynchronizedCollection
+    implements Collection, Serializable
   {
-    private Iterator i;
-
-    public UnmodifiableIterator(Iterator i)
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 3053995032091335093L;
+
+    /**
+     * The wrapped collection. Package visible for use by subclasses.
+     * @serial the real collection
+     */
+    final Collection c;
+
+    /**
+     * The object to synchronize on.  When an instance is created via public
+     * methods, it will be this; but other uses like SynchronizedMap.values()
+     * must specify another mutex. Package visible for use by subclasses.
+     * @serial the lock
+     */
+    final Object mutex;
+
+    /**
+     * Wrap a given collection.
+     * @param c the collection to wrap
+     * @throws NullPointerException if c is null
+     */
+    SynchronizedCollection(Collection c)
     {
-      this.i = i;
+      this.c = c;
+      mutex = this;
+      if (c == null)
+        throw new NullPointerException();
     }
 
-    public Object next()
+    /**
+     * Called only by trusted code to specify the mutex as well as the
+     * collection.
+     * @param sync the mutex
+     * @param c the collection
+     */
+    SynchronizedCollection(Object sync, Collection c)
     {
-      return i.next();
+      this.c = c;
+      mutex = sync;
     }
-    public boolean hasNext()
+
+    public boolean add(Object o)
     {
-      return i.hasNext();
+      synchronized (mutex)
+        {
+          return c.add(o);
+        }
     }
-    public void remove()
+
+    public boolean addAll(Collection col)
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return c.addAll(col);
+        }
     }
-  }
-
-  private static class UnmodifiableListIterator extends UnmodifiableIterator
-    implements ListIterator
-  {
-    // This is stored both here and in the superclass, to avoid excessive
-    // casting.
-    private ListIterator li;
 
-    public UnmodifiableListIterator(ListIterator li)
+    public void clear()
     {
-      super(li);
-      this.li = li;
+      synchronized (mutex)
+        {
+          c.clear();
+        }
     }
 
-    public boolean hasPrevious()
+    public boolean contains(Object o)
     {
-      return li.hasPrevious();
+      synchronized (mutex)
+        {
+          return c.contains(o);
+        }
     }
-    public Object previous()
+
+    public boolean containsAll(Collection c1)
     {
-      return li.previous();
+      synchronized (mutex)
+        {
+          return c.containsAll(c1);
+        }
     }
-    public int nextIndex()
+
+    public boolean isEmpty()
     {
-      return li.nextIndex();
+      synchronized (mutex)
+        {
+          return c.isEmpty();
+        }
     }
-    public int previousIndex()
-    {
-      return li.previousIndex();
-    }
-    public void add(Object o)
-    {
-      throw new UnsupportedOperationException();
-    }
-    public void set(Object o)
-    {
-      throw new UnsupportedOperationException();
-    }
-  }
 
-  private static class UnmodifiableCollection implements Collection,
-    Serializable
-  {
-    Collection c;
-
-    public UnmodifiableCollection(Collection c)
+    public Iterator iterator()
     {
-      this.c = c;
+      synchronized (mutex)
+        {
+          return new SynchronizedIterator(mutex, c.iterator());
+        }
     }
 
-    public boolean add(Object o)
-    {
-      throw new UnsupportedOperationException();
-    }
-    public boolean addAll(Collection c)
-    {
-      throw new UnsupportedOperationException();
-    }
-    public void clear()
-    {
-      throw new UnsupportedOperationException();
-    }
-    public boolean contains(Object o)
+    public boolean remove(Object o)
     {
-      return c.contains(o);
+      synchronized (mutex)
+        {
+          return c.remove(o);
+        }
     }
-    public boolean containsAll(Collection c1)
+
+    public boolean removeAll(Collection col)
     {
-      return c.containsAll(c1);
+      synchronized (mutex)
+        {
+          return c.removeAll(col);
+        }
     }
-    public boolean isEmpty()
+
+    public boolean retainAll(Collection col)
     {
-      return c.isEmpty();
+      synchronized (mutex)
+        {
+          return c.retainAll(col);
+        }
     }
-    public Iterator iterator()
+
+    public int size()
     {
-      return new UnmodifiableIterator(c.iterator());
+      synchronized (mutex)
+        {
+          return c.size();
+        }
     }
-    public boolean remove(Object o)
+
+    public Object[] toArray()
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return c.toArray();
+        }
     }
-    public boolean removeAll(Collection c)
+
+    public Object[] toArray(Object[] a)
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return c.toArray(a);
+        }
     }
-    public boolean retainAll(Collection c)
+
+    public String toString()
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return c.toString();
+        }
     }
-    public int size()
+  } // class SynchronizedCollection
+
+  /**
+   * The implementation of the various iterator methods in the
+   * synchronized classes. These iterators must "sync" on the same object
+   * as the collection they iterate over.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class SynchronizedIterator implements Iterator
+  {
+    /**
+     * The object to synchronize on. Package visible for use by subclass.
+     */
+    final Object mutex;
+
+    /**
+     * The wrapped iterator.
+     */
+    private final Iterator i;
+
+    /**
+     * Only trusted code creates a wrapper, with the specified sync.
+     * @param sync the mutex
+     * @param i the wrapped iterator
+     */
+    SynchronizedIterator(Object sync, Iterator i)
     {
-      return c.size();
+      this.i = i;
+      mutex = sync;
     }
-    public Object[] toArray()
+
+    public Object next()
     {
-      return c.toArray();
+      synchronized (mutex)
+        {
+          return i.next();
+        }
     }
-    public Object[] toArray(Object[]a)
+
+    public boolean hasNext()
     {
-      return c.toArray(a);
+      synchronized (mutex)
+        {
+          return i.hasNext();
+        }
     }
-    public String toString()
+
+    public void remove()
     {
-      return c.toString();
+      synchronized (mutex)
+        {
+          i.remove();
+        }
     }
+  } // class SynchronizedIterator
+
+  /**
+   * Returns a synchronized (thread-safe) list wrapper backed by the
+   * given list. Notice that element access through the iterators
+   * is thread-safe, but if the list can be structurally modified
+   * (adding or removing elements) then you should synchronize around the
+   * iteration to avoid non-deterministic behavior:<br>
+   * <pre>
+   * List l = Collections.synchronizedList(new List(...));
+   * ...
+   * synchronized (l)
+   *   {
+   *     Iterator i = l.iterator();
+   *     while (i.hasNext())
+   *       foo(i.next());
+   *   }
+   * </pre><p>
+   *
+   * The returned List implements Serializable, but can only be serialized if
+   * the list it wraps is likewise Serializable. In addition, if the wrapped
+   * list implements RandomAccess, this does too.
+   *
+   * @param l the list to wrap
+   * @return a synchronized view of the list
+   * @see Serializable
+   * @see RandomAccess
+   */
+  public static List synchronizedList(List l)
+  {
+    if (l instanceof RandomAccess)
+      return new SynchronizedRandomAccessList(l);
+    return new SynchronizedList(l);
   }
 
-  private static class UnmodifiableList extends UnmodifiableCollection
+  /**
+   * The implementation of {@link #synchronizedList(List)} for sequential
+   * lists. This class name is required for compatibility with Sun's JDK
+   * serializability. Package visible, so that lists such as Vector.subList()
+   * can specify which object to synchronize on.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  static class SynchronizedList extends SynchronizedCollection
     implements List
   {
-    // This is stored both here and in the superclass, to avoid excessive
-    // casting.
-    List l;
-
-    public UnmodifiableList(List l)
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -7754090372962971524L;
+
+    /**
+     * The wrapped list; stored both here and in the superclass to avoid
+     * excessive casting. Package visible for use by subclass.
+     * @serial the wrapped list
+     */
+    final List list;
+
+    /**
+     * Wrap a given list.
+     * @param l the list to wrap
+     * @throws NullPointerException if l is null
+     */
+    SynchronizedList(List l)
     {
       super(l);
-      this.l = l;
+      list = l;
+    }
+
+    /**
+     * Called only by trusted code to specify the mutex as well as the list.
+     * @param sync the mutex
+     * @param l the list
+     */
+    SynchronizedList(Object sync, List l)
+    {
+      super(sync, l);
+      list = l;
     }
 
     public void add(int index, Object o)
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          list.add(index, o);
+        }
     }
+
     public boolean addAll(int index, Collection c)
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return list.addAll(index, c);
+        }
     }
+
     public boolean equals(Object o)
     {
-      return l.equals(o);
+      synchronized (mutex)
+        {
+          return list.equals(o);
+        }
     }
+
     public Object get(int index)
     {
-      return l.get(index);
+      synchronized (mutex)
+        {
+          return list.get(index);
+        }
     }
+
     public int hashCode()
     {
-      return l.hashCode();
+      synchronized (mutex)
+        {
+          return list.hashCode();
+        }
     }
+
     public int indexOf(Object o)
     {
-      return l.indexOf(o);
+      synchronized (mutex)
+        {
+          return list.indexOf(o);
+        }
     }
+
     public int lastIndexOf(Object o)
     {
-      return l.lastIndexOf(o);
+      synchronized (mutex)
+        {
+          return list.lastIndexOf(o);
+        }
     }
+
     public ListIterator listIterator()
     {
-      return new UnmodifiableListIterator(l.listIterator());
+      synchronized (mutex)
+        {
+          return new SynchronizedListIterator(mutex, list.listIterator());
+        }
     }
+
     public ListIterator listIterator(int index)
     {
-      return new UnmodifiableListIterator(l.listIterator(index));
+      synchronized (mutex)
+        {
+          return new SynchronizedListIterator(mutex, list.listIterator(index));
+        }
     }
+
     public Object remove(int index)
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return list.remove(index);
+        }
     }
+
     public Object set(int index, Object o)
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return list.set(index, o);
+        }
     }
+
     public List subList(int fromIndex, int toIndex)
     {
-      return new UnmodifiableList(l.subList(fromIndex, toIndex));
+      synchronized (mutex)
+        {
+          return new SynchronizedList(mutex, list.subList(fromIndex, toIndex));
+        }
     }
-  }
+  } // class SynchronizedList
 
-  private static class UnmodifiableSet extends UnmodifiableCollection
-    implements Set
+  /**
+   * The implementation of {@link #synchronizedList(List)} for random-access
+   * lists. This class name is required for compatibility with Sun's JDK
+   * serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class SynchronizedRandomAccessList
+    extends SynchronizedList implements RandomAccess
   {
-    public UnmodifiableSet(Set s)
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 1530674583602358482L;
+
+    /**
+     * Wrap a given list.
+     * @param l the list to wrap
+     * @throws NullPointerException if l is null
+     */
+    SynchronizedRandomAccessList(List l)
     {
-      super(s);
+      super(l);
     }
-    public boolean equals(Object o)
+
+    /**
+     * Called only by trusted code to specify the mutex as well as the
+     * collection.
+     * @param sync the mutex
+     * @param l the list
+     */
+    SynchronizedRandomAccessList(Object sync, List l)
     {
-      return c.equals(o);
+      super(sync, l);
     }
-    public int hashCode()
+
+    public List subList(int fromIndex, int toIndex)
     {
-      return c.hashCode();
+      synchronized (mutex)
+        {
+          return new SynchronizedRandomAccessList(mutex,
+                                                  list.subList(fromIndex,
+                                                               toIndex));
+        }
     }
-  }
+  } // class SynchronizedRandomAccessList
 
-  private static class UnmodifiableSortedSet extends UnmodifiableSet
-    implements SortedSet
+  /**
+   * The implementation of {@link SynchronizedList#listIterator()}. This
+   * iterator must "sync" on the same object as the list it iterates over.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class SynchronizedListIterator
+    extends SynchronizedIterator implements ListIterator
   {
-    // This is stored both here and in the superclass, to avoid excessive
-    // casting.
-    private SortedSet ss;
-
-    public UnmodifiableSortedSet(SortedSet ss)
+    /**
+     * The wrapped iterator, stored both here and in the superclass to
+     * avoid excessive casting.
+     */
+    private final ListIterator li;
+
+    /**
+     * Only trusted code creates a wrapper, with the specified sync.
+     * @param sync the mutex
+     * @param li the wrapped iterator
+     */
+    SynchronizedListIterator(Object sync, ListIterator li)
     {
-      super(ss);
-      this.ss = ss;
+      super(sync, li);
+      this.li = li;
     }
 
-    public Comparator comparator()
+    public void add(Object o)
     {
-      return ss.comparator();
+      synchronized (mutex)
+        {
+          li.add(o);
+        }
     }
-    public Object first()
+    public boolean hasPrevious()
     {
-      return ss.first();
+      synchronized (mutex)
+        {
+          return li.hasPrevious();
+        }
     }
-    public Object last()
+
+    public int nextIndex()
     {
-      return ss.last();
+      synchronized (mutex)
+        {
+          return li.nextIndex();
+        }
     }
-    public SortedSet headSet(Object toElement)
+
+    public Object previous()
     {
-      return new UnmodifiableSortedSet(ss.headSet(toElement));
+      synchronized (mutex)
+        {
+          return li.previous();
+        }
     }
-    public SortedSet tailSet(Object fromElement)
+
+    public int previousIndex()
     {
-      return new UnmodifiableSortedSet(ss.tailSet(fromElement));
+      synchronized (mutex)
+        {
+          return li.previousIndex();
+        }
     }
-    public SortedSet subSet(Object fromElement, Object toElement)
+
+    public void set(Object o)
     {
-      return new UnmodifiableSortedSet(ss.subSet(fromElement, toElement));
+      synchronized (mutex)
+        {
+          li.set(o);
+        }
     }
+  } // class SynchronizedListIterator
+
+  /**
+   * Returns a synchronized (thread-safe) map wrapper backed by the given
+   * map. Notice that element access through the collection views and their
+   * iterators are thread-safe, but if the map can be structurally modified
+   * (adding or removing elements) then you should synchronize around the
+   * iteration to avoid non-deterministic behavior:<br>
+   * <pre>
+   * Map m = Collections.synchronizedMap(new Map(...));
+   * ...
+   * Set s = m.keySet(); // safe outside a synchronized block
+   * synchronized (m) // synch on m, not s
+   *   {
+   *     Iterator i = s.iterator();
+   *     while (i.hasNext())
+   *       foo(i.next());
+   *   }
+   * </pre><p>
+   *
+   * The returned Map implements Serializable, but can only be serialized if
+   * the map it wraps is likewise Serializable.
+   *
+   * @param m the map to wrap
+   * @return a synchronized view of the map
+   * @see Serializable
+   */
+  public static Map synchronizedMap(Map m)
+  {
+    return new SynchronizedMap(m);
   }
 
-  private static class UnmodifiableMap implements Map, Serializable
+  /**
+   * The implementation of {@link #synchronizedMap(Map)}. This
+   * class name is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class SynchronizedMap implements Map, Serializable
   {
-    Map m;
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 1978198479659022715L;
+
+    /**
+     * The wrapped map.
+     * @serial the real map
+     */
+    private final Map m;
+
+    /**
+     * The object to synchronize on.  When an instance is created via public
+     * methods, it will be this; but other uses like
+     * SynchronizedSortedMap.subMap() must specify another mutex. Package
+     * visible for use by subclass.
+     * @serial the lock
+     */
+    final Object mutex;
+
+    /**
+     * Cache the entry set.
+     */
+    private transient Set entries;
+
+    /**
+     * Cache the key set.
+     */
+    private transient Set keys;
+
+    /**
+     * Cache the value collection.
+     */
+    private transient Collection values;
+
+    /**
+     * Wrap a given map.
+     * @param m the map to wrap
+     * @throws NullPointerException if m is null
+     */
+    SynchronizedMap(Map m)
+    {
+      this.m = m;
+      mutex = this;
+      if (m == null)
+        throw new NullPointerException();
+    }
 
-    public UnmodifiableMap(Map m)
+    /**
+     * Called only by trusted code to specify the mutex as well as the map.
+     * @param sync the mutex
+     * @param m the map
+     */
+    SynchronizedMap(Object sync, Map m)
     {
       this.m = m;
+      mutex = sync;
     }
 
     public void clear()
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          m.clear();
+        }
     }
+
     public boolean containsKey(Object key)
     {
-      return m.containsKey(key);
+      synchronized (mutex)
+        {
+          return m.containsKey(key);
+        }
     }
+
     public boolean containsValue(Object value)
     {
-      return m.containsValue(value);
+      synchronized (mutex)
+        {
+          return m.containsValue(value);
+        }
     }
 
     // This is one of the ickiest cases of nesting I've ever seen. It just
-    // means "return an UnmodifiableSet, except that the iterator() method
-    // returns an UnmodifiableIterator whos next() method returns an
-    // unmodifiable wrapper around its normal return value".
+    // means "return a SynchronizedSet, except that the iterator() method
+    // returns an SynchronizedIterator whose next() method returns a
+    // synchronized wrapper around its normal return value".
     public Set entrySet()
     {
-      return new UnmodifiableSet(m.entrySet())
-      {
-       public Iterator iterator()
-       {
-         return new UnmodifiableIterator(c.iterator())
-         {
-           public Object next()
-           {
-             final Map.Entry e = (Map.Entry) super.next();
-             return new Map.Entry()
-             {
-               public Object getKey()
-               {
-                 return e.getKey();
-               }
-               public Object getValue()
-               {
-                 return e.getValue();
-               }
-               public Object setValue(Object value)
-               {
-                 throw new UnsupportedOperationException();
-               }
-               public int hashCode()
-               {
-                 return e.hashCode();
-               }
-               public boolean equals(Object o)
-               {
-                 return e.equals(o);
-               }
-             };
-           }
-         };
-       }
-      };
+      // Define this here to spare some nesting.
+      class SynchronizedMapEntry implements Map.Entry
+      {
+        final Map.Entry e;
+        SynchronizedMapEntry(Object o)
+        {
+          e = (Map.Entry) o;
+        }
+        public boolean equals(Object o)
+        {
+          synchronized (mutex)
+            {
+              return e.equals(o);
+            }
+        }
+        public Object getKey()
+        {
+          synchronized (mutex)
+            {
+              return e.getKey();
+            }
+        }
+        public Object getValue()
+        {
+          synchronized (mutex)
+            {
+              return e.getValue();
+            }
+        }
+        public int hashCode()
+        {
+          synchronized (mutex)
+            {
+              return e.hashCode();
+            }
+        }
+        public Object setValue(Object value)
+        {
+          synchronized (mutex)
+            {
+              return e.setValue(value);
+            }
+        }
+        public String toString()
+        {
+          synchronized (mutex)
+            {
+              return e.toString();
+            }
+        }
+      } // class SynchronizedMapEntry
+
+      // Now the actual code.
+      if (entries == null)
+        synchronized (mutex)
+          {
+            entries = new SynchronizedSet(mutex, m.entrySet())
+            {
+              public Iterator iterator()
+              {
+                synchronized (super.mutex)
+                  {
+                    return new SynchronizedIterator(super.mutex, c.iterator())
+                    {
+                      public Object next()
+                      {
+                        synchronized (super.mutex)
+                          {
+                            return new SynchronizedMapEntry(super.next());
+                          }
+                      }
+                    };
+                  }
+              }
+            };
+          }
+      return entries;
     }
+
     public boolean equals(Object o)
     {
-      return m.equals(o);
+      synchronized (mutex)
+        {
+          return m.equals(o);
+        }
     }
+
     public Object get(Object key)
     {
-      return m.get(key);
-    }
-    public Object put(Object key, Object value)
-    {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return m.get(key);
+        }
     }
+
     public int hashCode()
     {
-      return m.hashCode();
+      synchronized (mutex)
+        {
+          return m.hashCode();
+        }
     }
+
     public boolean isEmpty()
     {
-      return m.isEmpty();
+      synchronized (mutex)
+        {
+          return m.isEmpty();
+        }
     }
+
     public Set keySet()
     {
-      return new UnmodifiableSet(m.keySet());
+      if (keys == null)
+        synchronized (mutex)
+          {
+            keys = new SynchronizedSet(mutex, m.keySet());
+          }
+      return keys;
     }
-    public void putAll(Map m)
+
+    public Object put(Object key, Object value)
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return m.put(key, value);
+        }
+    }
+
+    public void putAll(Map map)
+    {
+      synchronized (mutex)
+        {
+          m.putAll(map);
+        }
     }
+
     public Object remove(Object o)
     {
-      throw new UnsupportedOperationException();
+      synchronized (mutex)
+        {
+          return m.remove(o);
+        }
     }
+
     public int size()
     {
-      return m.size();
+      synchronized (mutex)
+        {
+          return m.size();
+        }
     }
-    public Collection values()
+
+    public String toString()
     {
-      return new UnmodifiableCollection(m.values());
+      synchronized (mutex)
+        {
+          return m.toString();
+        }
     }
-    public String toString()
+
+    public Collection values()
     {
-      return m.toString();
+      if (values == null)
+        synchronized (mutex)
+          {
+            values = new SynchronizedCollection(mutex, m.values());
+          }
+      return values;
     }
-  }
+  } // class SynchronizedMap
 
-  private static class UnmodifiableSortedMap extends UnmodifiableMap
-    implements SortedMap
+  /**
+   * Returns a synchronized (thread-safe) set wrapper backed by the given
+   * set. Notice that element access through the iterator is thread-safe, but
+   * if the set can be structurally modified (adding or removing elements)
+   * then you should synchronize around the iteration to avoid
+   * non-deterministic behavior:<br>
+   * <pre>
+   * Set s = Collections.synchronizedSet(new Set(...));
+   * ...
+   * synchronized (s)
+   *   {
+   *     Iterator i = s.iterator();
+   *     while (i.hasNext())
+   *       foo(i.next());
+   *   }
+   * </pre><p>
+   *
+   * The returned Set implements Serializable, but can only be serialized if
+   * the set it wraps is likewise Serializable.
+   *
+   * @param s the set to wrap
+   * @return a synchronized view of the set
+   * @see Serializable
+   */
+  public static Set synchronizedSet(Set s)
   {
-    // This is stored both here and in the superclass, to avoid excessive
-    // casting.
-    private SortedMap sm;
+    return new SynchronizedSet(s);
+  }
 
-    public UnmodifiableSortedMap(SortedMap sm)
+  /**
+   * The implementation of {@link #synchronizedSet(Set)}. This class
+   * name is required for compatibility with Sun's JDK serializability.
+   * Package visible, so that sets such as Hashtable.keySet()
+   * can specify which object to synchronize on.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  static class SynchronizedSet extends SynchronizedCollection
+    implements Set
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 487447009682186044L;
+
+    /**
+     * Wrap a given set.
+     * @param s the set to wrap
+     * @throws NullPointerException if s is null
+     */
+    SynchronizedSet(Set s)
     {
-      super(sm);
-      this.sm = sm;
+      super(s);
     }
 
-    public Comparator comparator()
-    {
-      return sm.comparator();
-    }
-    public Object firstKey()
+    /**
+     * Called only by trusted code to specify the mutex as well as the set.
+     * @param sync the mutex
+     * @param s the set
+     */
+    SynchronizedSet(Object sync, Set s)
     {
-      return sm.firstKey();
+      super(sync, s);
     }
-    public Object lastKey()
+
+    public boolean equals(Object o)
     {
-      return sm.lastKey();
+      synchronized (mutex)
+        {
+          return c.equals(o);
+        }
     }
-    public SortedMap headMap(Object toKey)
+
+    public int hashCode()
     {
-      return new UnmodifiableSortedMap(sm.headMap(toKey));
+      synchronized (mutex)
+        {
+          return c.hashCode();
+        }
     }
-    public SortedMap tailMap(Object fromKey)
-    {
-      return new UnmodifiableSortedMap(sm.tailMap(fromKey));
+  } // class SynchronizedSet
+
+  /**
+   * Returns a synchronized (thread-safe) sorted map wrapper backed by the
+   * given map. Notice that element access through the collection views,
+   * subviews, and their iterators are thread-safe, but if the map can be
+   * structurally modified (adding or removing elements) then you should
+   * synchronize around the iteration to avoid non-deterministic behavior:<br>
+   * <pre>
+   * SortedMap m = Collections.synchronizedSortedMap(new SortedMap(...));
+   * ...
+   * Set s = m.keySet(); // safe outside a synchronized block
+   * SortedMap m2 = m.headMap(foo); // safe outside a synchronized block
+   * Set s2 = m2.keySet(); // safe outside a synchronized block
+   * synchronized (m) // synch on m, not m2, s or s2
+   *   {
+   *     Iterator i = s.iterator();
+   *     while (i.hasNext())
+   *       foo(i.next());
+   *     i = s2.iterator();
+   *     while (i.hasNext())
+   *       bar(i.next());
+   *   }
+   * </pre><p>
+   *
+   * The returned SortedMap implements Serializable, but can only be
+   * serialized if the map it wraps is likewise Serializable.
+   *
+   * @param m the sorted map to wrap
+   * @return a synchronized view of the sorted map
+   * @see Serializable
+   */
+  public static SortedMap synchronizedSortedMap(SortedMap m)
+  {
+    return new SynchronizedSortedMap(m);
+  }
+
+  /**
+   * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This
+   * class name is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class SynchronizedSortedMap extends SynchronizedMap
+    implements SortedMap
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -8798146769416483793L;
+
+    /**
+     * The wrapped map; stored both here and in the superclass to avoid
+     * excessive casting.
+     * @serial the wrapped map
+     */
+    private final SortedMap sm;
+
+    /**
+     * Wrap a given map.
+     * @param sm the map to wrap
+     * @throws NullPointerException if sm is null
+     */
+    SynchronizedSortedMap(SortedMap sm)
+    {
+      super(sm);
+      this.sm = sm;
     }
-    public SortedMap subMap(Object fromKey, Object toKey)
+
+    /**
+     * Called only by trusted code to specify the mutex as well as the map.
+     * @param sync the mutex
+     * @param sm the map
+     */
+    SynchronizedSortedMap(Object sync, SortedMap sm)
     {
-      return new UnmodifiableSortedMap(sm.subMap(fromKey, toKey));
+      super(sync, sm);
+      this.sm = sm;
     }
-  }
 
-  // All the "Synchronized" wrapper objects include a "sync" field which
-  // specifies what object to synchronize on. That way, nested wrappers such as
-  // UnmodifiableMap.keySet synchronize on the right things.
+    public Comparator comparator()
+    {
+      synchronized (mutex)
+        {
+          return sm.comparator();
+        }
+    }
 
-  private static class SynchronizedIterator implements Iterator
-  {
-    Object sync;
-    private Iterator i;
+    public Object firstKey()
+    {
+      synchronized (mutex)
+        {
+          return sm.firstKey();
+        }
+    }
 
-    public SynchronizedIterator(Object sync, Iterator i)
+    public SortedMap headMap(Object toKey)
     {
-      this.sync = sync;
-      this.i = i;
+      synchronized (mutex)
+        {
+          return new SynchronizedSortedMap(mutex, sm.headMap(toKey));
+        }
     }
 
-    public Object next()
+    public Object lastKey()
     {
-      synchronized(sync)
-      {
-       return i.next();
-      }
+      synchronized (mutex)
+        {
+          return sm.lastKey();
+        }
     }
-    public boolean hasNext()
+
+    public SortedMap subMap(Object fromKey, Object toKey)
     {
-      synchronized(sync)
-      {
-       return i.hasNext();
-      }
+      synchronized (mutex)
+        {
+          return new SynchronizedSortedMap(mutex, sm.subMap(fromKey, toKey));
+        }
     }
-    public void remove()
+
+    public SortedMap tailMap(Object fromKey)
     {
-      synchronized(sync)
-      {
-       i.remove();
-      }
+      synchronized (mutex)
+        {
+          return new SynchronizedSortedMap(mutex, sm.tailMap(fromKey));
+        }
     }
+  } // class SynchronizedSortedMap
+
+  /**
+   * Returns a synchronized (thread-safe) sorted set wrapper backed by the
+   * given set. Notice that element access through the iterator and through
+   * subviews are thread-safe, but if the set can be structurally modified
+   * (adding or removing elements) then you should synchronize around the
+   * iteration to avoid non-deterministic behavior:<br>
+   * <pre>
+   * SortedSet s = Collections.synchronizedSortedSet(new SortedSet(...));
+   * ...
+   * SortedSet s2 = s.headSet(foo); // safe outside a synchronized block
+   * synchronized (s) // synch on s, not s2
+   *   {
+   *     Iterator i = s2.iterator();
+   *     while (i.hasNext())
+   *       foo(i.next());
+   *   }
+   * </pre><p>
+   *
+   * The returned SortedSet implements Serializable, but can only be
+   * serialized if the set it wraps is likewise Serializable.
+   *
+   * @param s the sorted set to wrap
+   * @return a synchronized view of the sorted set
+   * @see Serializable
+   */
+  public static SortedSet synchronizedSortedSet(SortedSet s)
+  {
+    return new SynchronizedSortedSet(s);
   }
 
-  private static class SynchronizedListIterator extends SynchronizedIterator
-    implements ListIterator
+  /**
+   * The implementation of {@link #synchronizedSortedSet(SortedSet)}. This
+   * class name is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class SynchronizedSortedSet extends SynchronizedSet
+    implements SortedSet
   {
-    // This is stored both here and in the superclass, to avoid excessive
-    // casting.
-    private ListIterator li;
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 8695801310862127406L;
+
+    /**
+     * The wrapped set; stored both here and in the superclass to avoid
+     * excessive casting.
+     * @serial the wrapped set
+     */
+    private final SortedSet ss;
+
+    /**
+     * Wrap a given set.
+     * @param ss the set to wrap
+     * @throws NullPointerException if ss is null
+     */
+    SynchronizedSortedSet(SortedSet ss)
+    {
+      super(ss);
+      this.ss = ss;
+    }
 
-    public SynchronizedListIterator(Object sync, ListIterator li)
+    /**
+     * Called only by trusted code to specify the mutex as well as the set.
+     * @param sync the mutex
+     * @param l the list
+     */
+    SynchronizedSortedSet(Object sync, SortedSet ss)
     {
-      super(sync, li);
-      this.li = li;
+      super(sync, ss);
+      this.ss = ss;
     }
 
-    public boolean hasPrevious()
+    public Comparator comparator()
     {
-      synchronized(sync)
-      {
-       return li.hasPrevious();
-      }
+      synchronized (mutex)
+        {
+          return ss.comparator();
+        }
     }
-    public Object previous()
+
+    public Object first()
     {
-      synchronized(sync)
-      {
-       return li.previous();
-      }
+      synchronized (mutex)
+        {
+          return ss.first();
+        }
     }
-    public int nextIndex()
+
+    public SortedSet headSet(Object toElement)
     {
-      synchronized(sync)
-      {
-       return li.nextIndex();
-      }
+      synchronized (mutex)
+        {
+          return new SynchronizedSortedSet(mutex, ss.headSet(toElement));
+        }
     }
-    public int previousIndex()
+
+    public Object last()
     {
-      synchronized(sync)
-      {
-       return li.previousIndex();
-      }
+      synchronized (mutex)
+        {
+          return ss.last();
+        }
     }
-    public void add(Object o)
+
+    public SortedSet subSet(Object fromElement, Object toElement)
     {
-      synchronized(sync)
-      {
-       li.add(o);
-      }
+      synchronized (mutex)
+        {
+          return new SynchronizedSortedSet(mutex,
+                                           ss.subSet(fromElement, toElement));
+        }
     }
-    public void set(Object o)
+
+    public SortedSet tailSet(Object fromElement)
     {
-      synchronized(sync)
-      {
-       li.set(o);
-      }
+      synchronized (mutex)
+        {
+          return new SynchronizedSortedSet(mutex, ss.tailSet(fromElement));
+        }
     }
-  }
+  } // class SynchronizedSortedSet
 
   /**
-   * Package visible, so that collections such as the one for
-   * Hashtable.values() can specify which object to synchronize on.
+   * Returns an unmodifiable view of the given collection. This allows
+   * "read-only" access, although changes in the backing collection show up
+   * in this view. Attempts to modify the collection directly or via iterators
+   * will fail with {@link UnsupportedOperationException}.
+   * <p>
+   *
+   * Since the collection might be a List or a Set, and those have incompatible
+   * equals and hashCode requirements, this relies on Object's implementation
+   * rather than passing those calls on to the wrapped collection. The returned
+   * Collection implements Serializable, but can only be serialized if
+   * the collection it wraps is likewise Serializable.
+   *
+   * @param c the collection to wrap
+   * @return a read-only view of the collection
+   * @see Serializable
    */
-  static class SynchronizedCollection implements Collection,
-    Serializable
+  public static Collection unmodifiableCollection(Collection c)
   {
-    Object sync;
-    Collection c;
+    return new UnmodifiableCollection(c);
+  }
 
-    public SynchronizedCollection(Collection c)
-    {
-      this.sync = this;
-      this.c = c;
-    }
-    public SynchronizedCollection(Object sync, Collection c)
+  /**
+   * The implementation of {@link #unmodifiableCollection(Collection)}. This
+   * class name is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class UnmodifiableCollection
+    implements Collection, Serializable
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = 1820017752578914078L;
+
+    /**
+     * The wrapped collection. Package visible for use by subclasses.
+     * @serial the real collection
+     */
+    final Collection c;
+
+    /**
+     * Wrap a given collection.
+     * @param c the collection to wrap
+     * @throws NullPointerException if c is null
+     */
+    UnmodifiableCollection(Collection c)
     {
       this.c = c;
-      this.sync = sync;
+      if (c == null)
+        throw new NullPointerException();
     }
 
     public boolean add(Object o)
     {
-      synchronized(sync)
-      {
-       return c.add(o);
-      }
+      throw new UnsupportedOperationException();
     }
-    public boolean addAll(Collection col)
+
+    public boolean addAll(Collection c)
     {
-      synchronized(sync)
-      {
-       return c.addAll(col);
-      }
+      throw new UnsupportedOperationException();
     }
+
     public void clear()
     {
-      synchronized(sync)
-      {
-       c.clear();
-      }
+      throw new UnsupportedOperationException();
     }
+
     public boolean contains(Object o)
     {
-      synchronized(sync)
-      {
-       return c.contains(o);
-      }
+      return c.contains(o);
     }
+
     public boolean containsAll(Collection c1)
     {
-      synchronized(sync)
-      {
-       return c.containsAll(c1);
-      }
+      return c.containsAll(c1);
     }
+
     public boolean isEmpty()
     {
-      synchronized(sync)
-      {
-       return c.isEmpty();
-      }
+      return c.isEmpty();
     }
+
     public Iterator iterator()
     {
-      synchronized(sync)
-      {
-       return new SynchronizedIterator(sync, c.iterator());
-      }
+      return new UnmodifiableIterator(c.iterator());
     }
+
     public boolean remove(Object o)
     {
-      synchronized(sync)
-      {
-       return c.remove(o);
-      }
+      throw new UnsupportedOperationException();
     }
-    public boolean removeAll(Collection col)
+
+    public boolean removeAll(Collection c)
     {
-      synchronized(sync)
-      {
-       return c.removeAll(col);
-      }
+      throw new UnsupportedOperationException();
     }
-    public boolean retainAll(Collection col)
+
+    public boolean retainAll(Collection c)
     {
-      synchronized(sync)
-      {
-       return c.retainAll(col);
-      }
+      throw new UnsupportedOperationException();
     }
+
     public int size()
     {
-      synchronized(sync)
-      {
-       return c.size();
-      }
+      return c.size();
     }
+
     public Object[] toArray()
     {
-      synchronized(sync)
-      {
-       return c.toArray();
-      }
+      return c.toArray();
     }
-    public Object[] toArray(Object[]a)
+
+    public Object[] toArray(Object[] a)
     {
-      synchronized(sync)
-      {
-       return c.toArray(a);
-      }
+      return c.toArray(a);
     }
+
     public String toString()
     {
-      synchronized(sync)
-      {
-       return c.toString();
-      }
+      return c.toString();
     }
-  }
+  } // class UnmodifiableCollection
 
-  private static class SynchronizedList extends SynchronizedCollection
-    implements List
+  /**
+   * The implementation of the various iterator methods in the
+   * unmodifiable classes.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class UnmodifiableIterator implements Iterator
   {
-    // This is stored both here and in the superclass, to avoid excessive
-    // casting.
-    List l;
+    /**
+     * The wrapped iterator.
+     */
+    private final Iterator i;
 
-    public SynchronizedList(Object sync, List l)
+    /**
+     * Only trusted code creates a wrapper.
+     * @param i the wrapped iterator
+     */
+    UnmodifiableIterator(Iterator i)
     {
-      super(sync, l);
-      this.l = l;
+      this.i = i;
+    }
+
+    public Object next()
+    {
+      return i.next();
+    }
+
+    public boolean hasNext()
+    {
+      return i.hasNext();
+    }
+
+    public void remove()
+    {
+      throw new UnsupportedOperationException();
     }
-    public SynchronizedList(List l)
+  } // class UnmodifiableIterator
+
+  /**
+   * Returns an unmodifiable view of the given list. This allows
+   * "read-only" access, although changes in the backing list show up
+   * in this view. Attempts to modify the list directly, via iterators, or
+   * via sublists, will fail with {@link UnsupportedOperationException}.
+   * <p>
+   *
+   * The returned List implements Serializable, but can only be serialized if
+   * the list it wraps is likewise Serializable. In addition, if the wrapped
+   * list implements RandomAccess, this does too.
+   *
+   * @param l the list to wrap
+   * @return a read-only view of the list
+   * @see Serializable
+   * @see RandomAccess
+   */
+  public static List unmodifiableList(List l)
+  {
+    if (l instanceof RandomAccess)
+      return new UnmodifiableRandomAccessList(l);
+    return new UnmodifiableList(l);
+  }
+
+  /**
+   * The implementation of {@link #unmodifiableList(List)} for sequential
+   * lists. This class name is required for compatibility with Sun's JDK
+   * serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class UnmodifiableList extends UnmodifiableCollection
+    implements List
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -283967356065247728L;
+
+
+    /**
+     * The wrapped list; stored both here and in the superclass to avoid
+     * excessive casting. Package visible for use by subclass.
+     * @serial the wrapped list
+     */
+    final List list;
+
+    /**
+     * Wrap a given list.
+     * @param l the list to wrap
+     * @throws NullPointerException if l is null
+     */
+    UnmodifiableList(List l)
     {
       super(l);
-      this.l = l;
+      list = l;
     }
 
     public void add(int index, Object o)
     {
-      synchronized(sync)
-      {
-       l.add(index, o);
-      }
+      throw new UnsupportedOperationException();
     }
+
     public boolean addAll(int index, Collection c)
     {
-      synchronized(sync)
-      {
-       return l.addAll(index, c);
-      }
+      throw new UnsupportedOperationException();
     }
+
     public boolean equals(Object o)
     {
-      synchronized(sync)
-      {
-       return l.equals(o);
-      }
+      return list.equals(o);
     }
+
     public Object get(int index)
     {
-      synchronized(sync)
-      {
-       return l.get(index);
-      }
+      return list.get(index);
     }
+
     public int hashCode()
     {
-      synchronized(sync)
-      {
-       return l.hashCode();
-      }
+      return list.hashCode();
     }
+
     public int indexOf(Object o)
     {
-      synchronized(sync)
-      {
-       return l.indexOf(o);
-      }
+      return list.indexOf(o);
     }
+
     public int lastIndexOf(Object o)
     {
-      synchronized(sync)
-      {
-       return l.lastIndexOf(o);
-      }
+      return list.lastIndexOf(o);
     }
+
     public ListIterator listIterator()
     {
-      synchronized(sync)
-      {
-       return new SynchronizedListIterator(sync, l.listIterator());
-      }
+      return new UnmodifiableListIterator(list.listIterator());
     }
+
     public ListIterator listIterator(int index)
     {
-      synchronized(sync)
-      {
-       return new SynchronizedListIterator(sync, l.listIterator(index));
-      }
+      return new UnmodifiableListIterator(list.listIterator(index));
     }
+
     public Object remove(int index)
     {
-      synchronized(sync)
-      {
-       return l.remove(index);
-      }
-    }
-    public boolean remove(Object o)
-    {
-      synchronized(sync)
-      {
-       return l.remove(o);
-      }
+      throw new UnsupportedOperationException();
     }
+
     public Object set(int index, Object o)
     {
-      synchronized(sync)
-      {
-       return l.set(index, o);
-      }
+      throw new UnsupportedOperationException();
     }
+
     public List subList(int fromIndex, int toIndex)
     {
-      synchronized(sync)
-      {
-       return new SynchronizedList(l.subList(fromIndex, toIndex));
-      }
+      return unmodifiableList(list.subList(fromIndex, toIndex));
     }
-  }
+  } // class UnmodifiableList
 
   /**
-   * Package visible, so that sets such as the one for Hashtable.keySet()
-   * can specify which object to synchronize on.
+   * The implementation of {@link #unmodifiableList(List)} for random-access
+   * lists. This class name is required for compatibility with Sun's JDK
+   * serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
    */
-  static class SynchronizedSet extends SynchronizedCollection
-    implements Set
+  private static final class UnmodifiableRandomAccessList
+    extends UnmodifiableList implements RandomAccess
   {
-    public SynchronizedSet(Object sync, Set s)
-    {
-      super(sync, s);
-    }
-    public SynchronizedSet(Set s)
-    {
-      super(s);
-    }
-
-    public boolean equals(Object o)
-    {
-      synchronized(sync)
-      {
-       return c.equals(o);
-      }
-    }
-    public int hashCode()
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -2542308836966382001L;
+
+    /**
+     * Wrap a given list.
+     * @param l the list to wrap
+     * @throws NullPointerException if l is null
+     */
+    UnmodifiableRandomAccessList(List l)
     {
-      synchronized(sync)
-      {
-       return c.hashCode();
-      }
+      super(l);
     }
-  }
+  } // class UnmodifiableRandomAccessList
 
-  private static class SynchronizedSortedSet extends SynchronizedSet
-    implements SortedSet
+  /**
+   * The implementation of {@link UnmodifiableList#listIterator()}.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class UnmodifiableListIterator
+    extends UnmodifiableIterator implements ListIterator
   {
-    // This is stored both here and in the superclass, to avoid excessive
-    // casting.
-    private SortedSet ss;
-
-    public SynchronizedSortedSet(Object sync, SortedSet ss)
-    {
-      super(sync, ss);
-      this.ss = ss;
-    }
-    public SynchronizedSortedSet(SortedSet ss)
+    /**
+     * The wrapped iterator, stored both here and in the superclass to
+     * avoid excessive casting.
+     */
+    private final ListIterator li;
+
+    /**
+     * Only trusted code creates a wrapper.
+     * @param li the wrapped iterator
+     */
+    UnmodifiableListIterator(ListIterator li)
     {
-      super(ss);
-      this.ss = ss;
+      super(li);
+      this.li = li;
     }
 
-    public Comparator comparator()
+    public void add(Object o)
     {
-      synchronized(sync)
-      {
-       return ss.comparator();
-      }
+      throw new UnsupportedOperationException();
     }
-    public Object first()
+
+    public boolean hasPrevious()
     {
-      synchronized(sync)
-      {
-       return ss.first();
-      }
+      return li.hasPrevious();
     }
-    public Object last()
+
+    public int nextIndex()
     {
-      synchronized(sync)
-      {
-       return ss.last();
-      }
+      return li.nextIndex();
     }
-    public SortedSet headSet(Object toElement)
+
+    public Object previous()
     {
-      synchronized(sync)
-      {
-       return new SynchronizedSortedSet(sync, ss.headSet(toElement));
-      }
+      return li.previous();
     }
-    public SortedSet tailSet(Object fromElement)
+
+    public int previousIndex()
     {
-      synchronized(sync)
-      {
-       return new SynchronizedSortedSet(sync, ss.tailSet(fromElement));
-      }
+      return li.previousIndex();
     }
-    public SortedSet subSet(Object fromElement, Object toElement)
+
+    public void set(Object o)
     {
-      synchronized(sync)
-      {
-       return new SynchronizedSortedSet(sync,
-                                        ss.subSet(fromElement, toElement));
-      }
+      throw new UnsupportedOperationException();
     }
-  }
+  } // class UnmodifiableListIterator
 
-  private static class SynchronizedMap implements Map, Serializable
+  /**
+   * Returns an unmodifiable view of the given map. This allows "read-only"
+   * access, although changes in the backing map show up in this view.
+   * Attempts to modify the map directly, or via collection views or their
+   * iterators will fail with {@link UnsupportedOperationException}.
+   * <p>
+   *
+   * The returned Map implements Serializable, but can only be serialized if
+   * the map it wraps is likewise Serializable.
+   *
+   * @param m the map to wrap
+   * @return a read-only view of the map
+   * @see Serializable
+   */
+  public static Map unmodifiableMap(Map m)
   {
-    Object sync;
-    Map m;
+    return new UnmodifiableMap(m);
+  }
 
-    public SynchronizedMap(Object sync, Map m)
-    {
-      this.sync = sync;
-      this.m = m;
-    }
-    public SynchronizedMap(Map m)
+  /**
+   * The implementation of {@link #unmodifiableMap(Map)}. This
+   * class name is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class UnmodifiableMap implements Map, Serializable
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -1034234728574286014L;
+
+    /**
+     * The wrapped map.
+     * @serial the real map
+     */
+    private final Map m;
+
+    /**
+     * Cache the entry set.
+     */
+    private transient Set entries;
+
+    /**
+     * Cache the key set.
+     */
+    private transient Set keys;
+
+    /**
+     * Cache the value collection.
+     */
+    private transient Collection values;
+
+    /**
+     * Wrap a given map.
+     * @param m the map to wrap
+     * @throws NullPointerException if m is null
+     */
+    UnmodifiableMap(Map m)
     {
       this.m = m;
-      this.sync = this;
+      if (m == null)
+        throw new NullPointerException();
     }
 
     public void clear()
     {
-      synchronized(sync)
-      {
-       m.clear();
-      }
+      throw new UnsupportedOperationException();
     }
+
     public boolean containsKey(Object key)
     {
-      synchronized(sync)
-      {
-       return m.containsKey(key);
-      }
+      return m.containsKey(key);
     }
+
     public boolean containsValue(Object value)
     {
-      synchronized(sync)
-      {
-       return m.containsValue(value);
-      }
+      return m.containsValue(value);
     }
 
-    // This is one of the ickiest cases of nesting I've ever seen. It just
-    // means "return a SynchronizedSet, except that the iterator() method
-    // returns an SynchronizedIterator whos next() method returns a
-    // synchronized wrapper around its normal return value".
     public Set entrySet()
     {
-      synchronized(sync)
+      if (entries == null)
+        entries = new UnmodifiableEntrySet(m.entrySet());
+      return entries;
+    }
+
+    /**
+     * The implementation of {@link UnmodifiableMap#entrySet()}. This class
+     * name is required for compatibility with Sun's JDK serializability.
+     *
+     * @author Eric Blake <ebb9@email.byu.edu>
+     */
+    private static final class UnmodifiableEntrySet extends UnmodifiableSet
+      implements Serializable
+    {
+      /**
+       * Compatible with JDK 1.4.
+       */
+      private static final long serialVersionUID = 7854390611657943733L;
+
+      /**
+       * Wrap a given set.
+       * @param s the set to wrap
+       */
+      UnmodifiableEntrySet(Set s)
       {
-       return new SynchronizedSet(sync, m.entrySet())
+        super(s);
+      }
+
+      // The iterator must return unmodifiable map entries.
+      public Iterator iterator()
+      {
+        return new UnmodifiableIterator(c.iterator())
        {
-         public Iterator iterator()
-         {
-           synchronized(SynchronizedMap.this.sync)
+          public Object next()
+          {
+            final Map.Entry e = (Map.Entry) super.next();
+            return new Map.Entry()
            {
-             return new SynchronizedIterator(SynchronizedMap.this.sync,
-                                             c.iterator())
-             {
-               public Object next()
-               {
-                 synchronized(SynchronizedMap.this.sync)
-                 {
-                   final Map.Entry e = (Map.Entry) super.next();
-                   return new Map.Entry()
-                   {
-                     public Object getKey()
-                     {
-                       synchronized(SynchronizedMap.this.sync)
-                       {
-                         return e.getKey();
-                       }
-                     }
-                     public Object getValue()
-                     {
-                       synchronized(SynchronizedMap.this.sync)
-                       {
-                         return e.getValue();
-                       }
-                     }
-                     public Object setValue(Object value)
-                     {
-                       synchronized(SynchronizedMap.this.sync)
-                       {
-                         return e.setValue(value);
-                       }
-                     }
-                     public int hashCode()
-                     {
-                       synchronized(SynchronizedMap.this.sync)
-                       {
-                         return e.hashCode();
-                       }
-                     }
-                     public boolean equals(Object o)
-                     {
-                       synchronized(SynchronizedMap.this.sync)
-                       {
-                         return e.equals(o);
-                       }
-                     }
-                   };
-                 }
-               }
-             };
-           }
-         }
+              public boolean equals(Object o)
+              {
+                return e.equals(o);
+              }
+              public Object getKey()
+              {
+                return e.getKey();
+              }
+              public Object getValue()
+              {
+                return e.getValue();
+              }
+              public int hashCode()
+              {
+                return e.hashCode();
+              }
+              public Object setValue(Object value)
+              {
+                throw new UnsupportedOperationException();
+              }
+              public String toString()
+              {
+                return e.toString();
+              }
+           };
+          }
        };
       }
-    }
+    } // class UnmodifiableEntrySet
+
     public boolean equals(Object o)
     {
-      synchronized(sync)
-      {
-       return m.equals(o);
-      }
+      return m.equals(o);
     }
+
     public Object get(Object key)
     {
-      synchronized(sync)
-      {
-       return m.get(key);
-      }
+      return m.get(key);
     }
+
     public Object put(Object key, Object value)
     {
-      synchronized(sync)
-      {
-       return m.put(key, value);
-      }
+      throw new UnsupportedOperationException();
     }
+
     public int hashCode()
     {
-      synchronized(sync)
-      {
-       return m.hashCode();
-      }
+      return m.hashCode();
     }
+
     public boolean isEmpty()
     {
-      synchronized(sync)
-      {
-       return m.isEmpty();
-      }
+      return m.isEmpty();
     }
+
     public Set keySet()
     {
-      synchronized(sync)
-      {
-       return new SynchronizedSet(sync, m.keySet());
-      }
+      if (keys == null)
+        keys = new UnmodifiableSet(m.keySet());
+      return keys;
     }
-    public void putAll(Map map)
+
+    public void putAll(Map m)
     {
-      synchronized(sync)
-      {
-       m.putAll(map);
-      }
+      throw new UnsupportedOperationException();
     }
+
     public Object remove(Object o)
     {
-      synchronized(sync)
-      {
-       return m.remove(o);
-      }
+      throw new UnsupportedOperationException();
     }
 
     public int size()
     {
-      synchronized(sync)
-      {
-       return m.size();
-      }
+      return m.size();
     }
-    public Collection values()
+
+    public String toString()
     {
-      synchronized(sync)
-      {
-       return new SynchronizedCollection(sync, m.values());
-      }
+      return m.toString();
     }
-    public String toString()
+
+    public Collection values()
     {
-      synchronized(sync)
-      {
-       return m.toString();
-      }
+      if (values == null)
+        values = new UnmodifiableCollection(m.values());
+      return values;
     }
+  } // class UnmodifiableMap
+
+  /**
+   * Returns an unmodifiable view of the given set. This allows
+   * "read-only" access, although changes in the backing set show up
+   * in this view. Attempts to modify the set directly or via iterators
+   * will fail with {@link UnsupportedOperationException}.
+   * <p>
+   *
+   * The returned Set implements Serializable, but can only be serialized if
+   * the set it wraps is likewise Serializable.
+   *
+   * @param s the set to wrap
+   * @return a read-only view of the set
+   * @see Serializable
+   */
+  public static Set unmodifiableSet(Set s)
+  {
+    return new UnmodifiableSet(s);
   }
 
-  private static class SynchronizedSortedMap extends SynchronizedMap
-    implements SortedMap
+  /**
+   * The implementation of {@link #unmodifiableSet(Set)}. This class
+   * name is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class UnmodifiableSet extends UnmodifiableCollection
+    implements Set
   {
-    // This is stored both here and in the superclass, to avoid excessive
-    // casting.
-    private SortedMap sm;
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -9215047833775013803L;
+
+    /**
+     * Wrap a given set.
+     * @param s the set to wrap
+     * @throws NullPointerException if s is null
+     */
+    UnmodifiableSet(Set s)
+    {
+      super(s);
+    }
 
-    public SynchronizedSortedMap(Object sync, SortedMap sm)
+    public boolean equals(Object o)
     {
-      super(sync, sm);
-      this.sm = sm;
+      return c.equals(o);
     }
-    public SynchronizedSortedMap(SortedMap sm)
+
+    public int hashCode()
+    {
+      return c.hashCode();
+    }
+  } // class UnmodifiableSet
+
+  /**
+   * Returns an unmodifiable view of the given sorted map. This allows
+   * "read-only" access, although changes in the backing map show up in this
+   * view. Attempts to modify the map directly, via subviews, via collection
+   * views, or iterators, will fail with {@link UnsupportedOperationException}.
+   * <p>
+   *
+   * The returned SortedMap implements Serializable, but can only be
+   * serialized if the map it wraps is likewise Serializable.
+   *
+   * @param m the map to wrap
+   * @return a read-only view of the map
+   * @see Serializable
+   */
+  public static SortedMap unmodifiableSortedMap(SortedMap m)
+  {
+    return new UnmodifiableSortedMap(m);
+  }
+
+  /**
+   * The implementation of {@link #unmodifiableSortedMap(SortedMap)}. This
+   * class name is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class UnmodifiableSortedMap extends UnmodifiableMap
+    implements SortedMap
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -8806743815996713206L;
+
+    /**
+     * The wrapped map; stored both here and in the superclass to avoid
+     * excessive casting.
+     * @serial the wrapped map
+     */
+    private final SortedMap sm;
+
+    /**
+     * Wrap a given map.
+     * @param sm the map to wrap
+     * @throws NullPointerException if sm is null
+     */
+    UnmodifiableSortedMap(SortedMap sm)
     {
       super(sm);
       this.sm = sm;
@@ -1827,36 +3350,114 @@ public class Collections
 
     public Comparator comparator()
     {
-      synchronized(sync)
-      {
-       return sm.comparator();
-      }
+      return sm.comparator();
     }
+
     public Object firstKey()
     {
-      synchronized(sync)
-      {
-       return sm.firstKey();
-      }
+      return sm.firstKey();
     }
+
+    public SortedMap headMap(Object toKey)
+    {
+      return new UnmodifiableSortedMap(sm.headMap(toKey));
+    }
+
     public Object lastKey()
     {
-      synchronized(sync)
-      {
-       return sm.lastKey();
-      }
+      return sm.lastKey();
     }
-    public SortedMap headMap(Object toKey)
+
+    public SortedMap subMap(Object fromKey, Object toKey)
     {
-      return new SynchronizedSortedMap(sync, sm.headMap(toKey));
+      return new UnmodifiableSortedMap(sm.subMap(fromKey, toKey));
     }
+
     public SortedMap tailMap(Object fromKey)
     {
-      return new SynchronizedSortedMap(sync, sm.tailMap(fromKey));
+      return new UnmodifiableSortedMap(sm.tailMap(fromKey));
     }
-    public SortedMap subMap(Object fromKey, Object toKey)
+  } // class UnmodifiableSortedMap
+
+  /**
+   * Returns an unmodifiable view of the given sorted set. This allows
+   * "read-only" access, although changes in the backing set show up
+   * in this view. Attempts to modify the set directly, via subsets, or via
+   * iterators, will fail with {@link UnsupportedOperationException}.
+   * <p>
+   *
+   * The returns SortedSet implements Serializable, but can only be
+   * serialized if the set it wraps is likewise Serializable.
+   *
+   * @param s the set to wrap
+   * @return a read-only view of the set
+   * @see Serializable
+   */
+  public static SortedSet unmodifiableSortedSet(SortedSet s)
+  {
+    return new UnmodifiableSortedSet(s);
+  }
+
+  /**
+   * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This
+   * class name is required for compatibility with Sun's JDK serializability.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static class UnmodifiableSortedSet extends UnmodifiableSet
+    implements SortedSet
+  {
+    /**
+     * Compatible with JDK 1.4.
+     */
+    private static final long serialVersionUID = -4929149591599911165L;
+
+    /**
+     * The wrapped set; stored both here and in the superclass to avoid
+     * excessive casting.
+     * @serial the wrapped set
+     */
+    private SortedSet ss;
+
+    /**
+     * Wrap a given set.
+     * @param ss the set to wrap
+     * @throws NullPointerException if ss is null
+     */
+    UnmodifiableSortedSet(SortedSet ss)
     {
-      return new SynchronizedSortedMap(sync, sm.subMap(fromKey, toKey));
+      super(ss);
+      this.ss = ss;
     }
-  }
-}
+
+    public Comparator comparator()
+    {
+      return ss.comparator();
+    }
+
+    public Object first()
+    {
+      return ss.first();
+    }
+
+    public SortedSet headSet(Object toElement)
+    {
+      return new UnmodifiableSortedSet(ss.headSet(toElement));
+    }
+
+    public Object last()
+    {
+      return ss.last();
+    }
+
+    public SortedSet subSet(Object fromElement, Object toElement)
+    {
+      return new UnmodifiableSortedSet(ss.subSet(fromElement, toElement));
+    }
+
+    public SortedSet tailSet(Object fromElement)
+    {
+      return new UnmodifiableSortedSet(ss.tailSet(fromElement));
+    }
+  } // class UnmodifiableSortedSet
+} // class Collections
index 7530e0833512745e173e340ba10f21fcc7590707..ef727c71c8b42fcd2650b6bfd58e36c887b75cf0 100644 (file)
@@ -1,6 +1,6 @@
 /* Dictionary.java -- an abstract (and essentially worthless) 
    class which is Hashtable's superclass
-   Copyright (C) 1998 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -35,49 +35,88 @@ package java.util;
  * This is an abstract class which has really gone by the wayside.
  * People at Javasoft are probably embarrassed by it.  At this point,
  * it might as well be an interface rather than a class, but it remains
- * this poor, laugable skeleton for the sake of backwards compatibility.
+ * this poor, laughable skeleton for the sake of backwards compatibility.
  * At any rate, this was what came before the <pre>Map</pre> interface 
  * in the Collections framework.
  *
- * @author      Jon Zeppieri
+ * @author Jon Zeppieri
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Map
+ * @see Hashtable
+ * @since 1.0
+ * @status updated to 1.4
  */
 public abstract class Dictionary extends Object
 {
-  /** returns an Enumeration of the values in this Dictionary */
+  /**
+   * Sole constructor (often called implicitly).
+   */
+  public Dictionary()
+  {
+  }
+
+  /**
+   * Returns an Enumeration of the values in this Dictionary.
+   *
+   * @return an Enumeration of the values
+   * @see #keys()
+   */
   public abstract Enumeration elements();
 
   /** 
-   * returns the value associated with the supplied key, or null
-   * if no such value exists
+   * Returns the value associated with the supplied key, or null
+   * if no such value exists. Since Dictionaries are not allowed null keys
+   * or elements, a null result always means the key is not present.
    *
-   * @param    key      the key to use to fetch the value
+   * @param key the key to use to fetch the value
+   * @return the mapped value
+   * @throws NullPointerException if key is null
+   * @see #put(Object, Object)
    */
   public abstract Object get(Object key);
 
-  /** returns true IFF there are no elements in this Dictionary (size() == 0) */
+  /**
+   * Returns true when there are no elements in this Dictionary.
+   *
+   * @return <code>size() == 0</code>
+   */
   public abstract boolean isEmpty();
 
-  /** returns an Enumeration of the keys in this Dictionary */
+  /**
+   * Returns an Enumeration of the keys in this Dictionary
+   *
+   * @return an Enumeration of the keys
+   * @see #elements()
+   */
   public abstract Enumeration keys();
 
   /**
-   * inserts a new value into this Dictionary, located by the
-   * supllied key; note: Dictionary's subclasses (all 1 of them)
-   * do not support null keys or values (I can only assume this
-   * would have been more general) 
+   * Inserts a new value into this Dictionary, located by the
+   * supplied key. Dictionary does not support null keys or values, so
+   * a null return can safely be interpreted as adding a new key.
    *
-   * @param      key      the key which locates the value
-   * @param      value    the value to put into the Dictionary
+   * @param key the key which locates the value
+   * @param value the value to put into the Dictionary
+   * @return the previous value of the key, or null if there was none
+   * @throws NullPointerException if key or value is null
+   * @see #get(Object)
    */
   public abstract Object put(Object key, Object value);
 
   /**
-   * removes fro the Dictionary the value located by the given key
+   * Removes from the Dictionary the value located by the given key. A null
+   * return safely means that the key was not mapped in the Dictionary.
    *
-   * @param       key      the key used to locate the value to be removed
+   * @param key the key used to locate the value to be removed
+   * @return the value associated with the removed key
+   * @throws NullPointerException if key is null
    */
   public abstract Object remove(Object key);
 
-  /** returns the number of values currently in this Dictionary */
+  /**
+   * Returns the number of values currently in this Dictionary.
+   *
+   * @return the number of keys in the Dictionary
+   */
   public abstract int size();
 }
index 3b351058a95929057eb75babac6af4348772216e..dcf7e7e340b9fb8e8473e129575a291f49081242 100644 (file)
@@ -53,14 +53,16 @@ import java.io.ObjectOutputStream;
  * <p>
  *
  * Under ideal circumstances (no collisions), HashMap offers O(1)
- * performance on most operations (<pre>containsValue()</pre> is,
+ * performance on most operations (<code>containsValue()</code> is,
  * of course, O(n)).  In the worst case (all keys map to the same
  * hash code -- very unlikely), most operations are O(n).
  * <p>
  *
  * HashMap is part of the JDK1.2 Collections API.  It differs from
  * Hashtable in that it accepts the null key and null values, and it
- * does not support "Enumeration views."
+ * does not support "Enumeration views." Also, it is not synchronized;
+ * if you plan to use it in multiple threads, consider using:<br>
+ * <code>Map m = Collections.synchronizedMap(new HashMap(...));</code>
  * <p>
  *
  * The iterators are <i>fail-fast</i>, meaning that any structural
@@ -81,6 +83,7 @@ import java.io.ObjectOutputStream;
  * @see IdentityHashMap
  * @see Hashtable
  * @since 1.2
+ * @status updated to 1.4
  */
 public class HashMap extends AbstractMap
   implements Map, Cloneable, Serializable
@@ -88,19 +91,16 @@ public class HashMap extends AbstractMap
   /**
    * Default number of buckets. This is the value the JDK 1.3 uses. Some
    * early documentation specified this value as 101. That is incorrect.
+   * Package visible for use by HashSet.
    */
   static final int DEFAULT_CAPACITY = 11;
 
   /**
    * The default load factor; this is explicitly specified by the spec.
+   * Package visible for use by HashSet.
    */
   static final float DEFAULT_LOAD_FACTOR = 0.75f;
 
-  /** "enum" of iterator types. */
-  static final int KEYS = 0,
-                   VALUES = 1,
-                   ENTRIES = 2;
-
   /**
    * Compatible with JDK 1.2.
    */
@@ -108,41 +108,54 @@ public class HashMap extends AbstractMap
 
   /**
    * The rounded product of the capacity and the load factor; when the number
-   * of elements exceeds the threshold, the HashMap calls <pre>rehash()</pre>.
-   * @serial
+   * of elements exceeds the threshold, the HashMap calls
+   * <code>rehash()</code>.
+   * @serial the threshold for rehashing
    */
-  int threshold;
+  private int threshold;
 
   /**
    * Load factor of this HashMap:  used in computing the threshold.
-   * @serial
+   * Package visible for use by HashSet.
+   * @serial the load factor
    */
   final float loadFactor;
 
   /**
    * Array containing the actual key-value mappings.
+   * Package visible for use by nested and subclasses.
    */
   transient HashEntry[] buckets;
 
   /**
    * Counts the number of modifications this HashMap has undergone, used
    * by Iterators to know when to throw ConcurrentModificationExceptions.
+   * Package visible for use by nested and subclasses.
    */
   transient int modCount;
 
   /**
    * The size of this HashMap:  denotes the number of key-value pairs.
+   * Package visible for use by nested and subclasses.
    */
   transient int size;
 
+  /**
+   * The cache for {@link #entrySet()}.
+   */
+  private transient Set entries;
+
   /**
    * Class to represent an entry in the hash table. Holds a single key-value
-   * pair.  This is extended again in LinkedHashMap.  See {@link clone()}
-   * for why this must be Cloneable.
+   * pair. Package visible for use by subclass.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
    */
-  static class HashEntry extends BasicMapEntry implements Cloneable
+  static class HashEntry extends BasicMapEntry
   {
-    /** The next entry in the linked list. */
+    /**
+     * The next entry in the linked list. Package visible for use by subclass.
+     */
     HashEntry next;
 
     /**
@@ -158,7 +171,8 @@ public class HashMap extends AbstractMap
     /**
      * Called when this entry is removed from the map. This version simply
      * returns the value, but in LinkedHashMap, it must also do bookkeeping.
-     * @return the value of this key as it is removed.
+     *
+     * @return the value of this key as it is removed
      */
     Object cleanup()
     {
@@ -182,9 +196,8 @@ public class HashMap extends AbstractMap
    *
    * Every element in Map m will be put into this new HashMap.
    *
-   * @param m a Map whose key / value pairs will be put into
-   *          the new HashMap.  <b>NOTE: key / value pairs
-   *          are not cloned in this constructor.</b>
+   * @param m a Map whose key / value pairs will be put into the new HashMap.
+   *        <b>NOTE: key / value pairs are not cloned in this constructor.</b>
    * @throws NullPointerException if m is null
    */
   public HashMap(Map m)
@@ -197,8 +210,8 @@ public class HashMap extends AbstractMap
    * Construct a new HashMap with a specific inital capacity and
    * default load factor of 0.75.
    *
-   * @param initialCapacity the initial capacity of this HashMap (>=0)
-   * @throws IllegalArgumentException if (initialCapacity < 0)
+   * @param initialCapacity the initial capacity of this HashMap (&gt;=0)
+   * @throws IllegalArgumentException if (initialCapacity &lt; 0)
    */
   public HashMap(int initialCapacity)
   {
@@ -208,10 +221,10 @@ public class HashMap extends AbstractMap
   /**
    * Construct a new HashMap with a specific inital capacity and load factor.
    *
-   * @param initialCapacity the initial capacity (>=0)
-   * @param loadFactor the load factor (>0, not NaN)
-   * @throws IllegalArgumentException if (initialCapacity < 0) ||
-   *                                     ! (loadFactor > 0.0)
+   * @param initialCapacity the initial capacity (&gt;=0)
+   * @param loadFactor the load factor (&gt; 0, not NaN)
+   * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
+   *                                     ! (loadFactor &gt; 0.0)
    */
   public HashMap(int initialCapacity, float loadFactor)
   {
@@ -229,7 +242,8 @@ public class HashMap extends AbstractMap
   }
 
   /**
-   * Returns the number of kay-value mappings currently in this Map
+   * Returns the number of kay-value mappings currently in this Map.
+   *
    * @return the size
    */
   public int size()
@@ -238,7 +252,8 @@ public class HashMap extends AbstractMap
   }
 
   /**
-   * Returns true if there are no key-value mappings currently in this Map
+   * Returns true if there are no key-value mappings currently in this Map.
+   *
    * @return <code>size() == 0</code>
    */
   public boolean isEmpty()
@@ -247,29 +262,31 @@ public class HashMap extends AbstractMap
   }
 
   /**
-   * Returns true if this HashMap contains a value <pre>o</pre>, such that
-   * <pre>o.equals(value)</pre>.
+   * Return the value in this HashMap associated with the supplied key,
+   * or <code>null</code> if the key maps to nothing.  NOTE: Since the value
+   * could also be null, you must use containsKey to see if this key
+   * actually maps to something.
    *
-   * @param value the value to search for in this HashMap
-   * @return true if at least one key maps to the value
+   * @param key the key for which to fetch an associated value
+   * @return what the key maps to, if present
+   * @see #put(Object, Object)
+   * @see #containsKey(Object)
    */
-  public boolean containsValue(Object value)
+  public Object get(Object key)
   {
-    for (int i = buckets.length - 1; i >= 0; i--)
+    int idx = hash(key);
+    HashEntry e = buckets[idx];
+    while (e != null)
       {
-        HashEntry e = buckets[i];
-        while (e != null)
-          {
-            if (value == null ? e.value == null : value.equals(e.value))
-              return true;
-            e = e.next;
-          }
+        if (equals(key, e.key))
+          return e.value;
+        e = e.next;
       }
-    return false;
+    return null;
   }
 
   /**
-   * Returns true if the supplied object <pre>equals()</pre> a key
+   * Returns true if the supplied object <code>equals()</code> a key
    * in this HashMap.
    *
    * @param key the key to search for in this HashMap
@@ -282,37 +299,13 @@ public class HashMap extends AbstractMap
     HashEntry e = buckets[idx];
     while (e != null)
       {
-        if (key == null ? e.key == null : key.equals(e.key))
+        if (equals(key, e.key))
           return true;
         e = e.next;
       }
     return false;
   }
 
-  /**
-   * Return the value in this HashMap associated with the supplied key,
-   * or <pre>null</pre> if the key maps to nothing.  NOTE: Since the value
-   * could also be null, you must use containsKey to see if this key
-   * actually maps to something.
-   *
-   * @param key the key for which to fetch an associated value
-   * @return what the key maps to, if present
-   * @see #put(Object, Object)
-   * @see #containsKey(Object)
-   */
-  public Object get(Object key)
-  {
-    int idx = hash(key);
-    HashEntry e = buckets[idx];
-    while (e != null)
-      {
-        if (key == null ? e.key == null : key.equals(e.key))
-          return e.value;
-        e = e.next;
-      }
-    return null;
-  }
-
   /**
    * Puts the supplied value into the Map, mapped by the supplied key.
    * The value may be retrieved by any object which <code>equals()</code>
@@ -328,13 +321,12 @@ public class HashMap extends AbstractMap
    */
   public Object put(Object key, Object value)
   {
-    modCount++;
     int idx = hash(key);
     HashEntry e = buckets[idx];
 
     while (e != null)
       {
-        if (key == null ? e.key == null : key.equals(e.key))
+        if (equals(key, e.key))
           // Must use this method for necessary bookkeeping in LinkedHashMap.
           return e.setValue(value);
         else
@@ -342,6 +334,7 @@ public class HashMap extends AbstractMap
       }
 
     // At this point, we know we need to add a new entry.
+    modCount++;
     if (++size > threshold)
       {
         rehash();
@@ -355,27 +348,36 @@ public class HashMap extends AbstractMap
   }
 
   /**
-   * Helper method for put, that creates and adds a new Entry.  This is
-   * overridden in LinkedHashMap for bookkeeping purposes.
+   * Copies all elements of the given map into this hashtable.  If this table
+   * already has a mapping for a key, the new mapping replaces the current
+   * one.
    *
-   * @param key the key of the new Entry
-   * @param value the value
-   * @param idx the index in buckets where the new Entry belongs
-   * @param callRemove Whether to call the removeEldestEntry method.
-   * @see #put(Object, Object)
+   * @param m the map to be hashed into this
    */
-  void addEntry(Object key, Object value, int idx, boolean callRemove)
+  public void putAll(Map m)
   {
-    HashEntry e = new HashEntry(key, value);
+    Iterator itr = m.entrySet().iterator();
 
-    e.next = buckets[idx];
-    buckets[idx] = e;
+    for (int msize = m.size(); msize > 0; msize--)
+      {
+        Map.Entry e = (Map.Entry) itr.next();
+        // Optimize in case the Entry is one of our own.
+        if (e instanceof BasicMapEntry)
+          {
+            BasicMapEntry entry = (BasicMapEntry) e;
+            put(entry.key, entry.value);
+          }
+        else
+          {
+            put(e.getKey(), e.getValue());
+          }
+      }
   }
-
+  
   /**
    * Removes from the HashMap and returns the value which is mapped by the
    * supplied key. If the key maps to nothing, then the HashMap remains
-   * unchanged, and <pre>null</pre> is returned. NOTE: Since the value
+   * unchanged, and <code>null</code> is returned. NOTE: Since the value
    * could also be null, you must use containsKey to see if you are
    * actually removing a mapping.
    *
@@ -384,15 +386,15 @@ public class HashMap extends AbstractMap
    */
   public Object remove(Object key)
   {
-    modCount++;
     int idx = hash(key);
     HashEntry e = buckets[idx];
     HashEntry last = null;
 
     while (e != null)
       {
-        if (key == null ? e.key == null : key.equals(e.key))
+        if (equals(key, e.key))
           {
+            modCount++;
             if (last == null)
               buckets[idx] = e.next;
             else
@@ -408,40 +410,39 @@ public class HashMap extends AbstractMap
   }
 
   /**
-   * Copies all elements of the given map into this hashtable.  If this table
-   * already has a mapping for a key, the new mapping replaces the current
-   * one.
-   *
-   * @param m the map to be hashed into this
+   * Clears the Map so it has no keys. This is O(1).
    */
-  public void putAll(Map m)
+  public void clear()
   {
-    Iterator itr = m.entrySet().iterator();
-
-    for (int msize = m.size(); msize > 0; msize--)
+    if (size != 0)
       {
-        Map.Entry e = (Map.Entry) itr.next();
-        // Optimize in case the Entry is one of our own.
-        if (e instanceof BasicMapEntry)
-          {
-            BasicMapEntry entry = (BasicMapEntry) e;
-            put(entry.key, entry.value);
-          }
-        else
-          {
-            put(e.getKey(), e.getValue());
-          }
+        modCount++;
+        Arrays.fill(buckets, null);
+        size = 0;
       }
   }
-  
+
   /**
-   * Clears the Map so it has no keys. This is O(1).
+   * Returns true if this HashMap contains a value <code>o</code>, such that
+   * <code>o.equals(value)</code>.
+   *
+   * @param value the value to search for in this HashMap
+   * @return true if at least one key maps to the value
+   * @see containsKey(Object)
    */
-  public void clear()
+  public boolean containsValue(Object value)
   {
-    modCount++;
-    Arrays.fill(buckets, null);
-    size = 0;
+    for (int i = buckets.length - 1; i >= 0; i--)
+      {
+        HashEntry e = buckets[i];
+        while (e != null)
+          {
+            if (equals(value, e.value))
+              return true;
+            e = e.next;
+          }
+      }
+    return false;
   }
 
   /**
@@ -463,6 +464,8 @@ public class HashMap extends AbstractMap
       }
     copy.buckets = new HashEntry[buckets.length];
     copy.putAllInternal(this);
+    // Clear the entry cache. AbstractMap.clone() does the others.
+    copy.entries = null;
     return copy;
   }
 
@@ -477,41 +480,43 @@ public class HashMap extends AbstractMap
    */
   public Set keySet()
   {
-    // Create an AbstractSet with custom implementations of those methods that
-    // can be overridden easily and efficiently.
-    return new AbstractSet()
-    {
-      public int size()
-      {
-        return size;
-      }
-
-      public Iterator iterator()
-      {
-        // Cannot create the iterator directly, because of LinkedHashMap.
-        return HashMap.this.iterator(KEYS);
-      }
-
-      public void clear()
-      {
-        HashMap.this.clear();
-      }
-
-      public boolean contains(Object o)
-      {
-        return HashMap.this.containsKey(o);
-      }
-
-      public boolean remove(Object o)
+    if (keys == null)
+      // Create an AbstractSet with custom implementations of those methods
+      // that can be overridden easily and efficiently.
+      keys = new AbstractSet()
       {
-        // Test against the size of the HashMap to determine if anything
-        // really got removed. This is necessary because the return value of
-        // HashMap.remove() is ambiguous in the null case.
-        int oldsize = size;
-        HashMap.this.remove(o);
-        return (oldsize != size);
-      }
-    };
+        public int size()
+        {
+          return size;
+        }
+
+        public Iterator iterator()
+        {
+          // Cannot create the iterator directly, because of LinkedHashMap.
+          return HashMap.this.iterator(KEYS);
+        }
+
+        public void clear()
+        {
+          HashMap.this.clear();
+        }
+
+        public boolean contains(Object o)
+        {
+          return containsKey(o);
+        }
+
+        public boolean remove(Object o)
+        {
+          // Test against the size of the HashMap to determine if anything
+          // really got removed. This is neccessary because the return value
+          // of HashMap.remove() is ambiguous in the null case.
+          int oldsize = size;
+          HashMap.this.remove(o);
+          return oldsize != size;
+        }
+      };
+    return keys;
   }
 
   /**
@@ -526,33 +531,34 @@ public class HashMap extends AbstractMap
    */
   public Collection values()
   {
-    // We don't bother overriding many of the optional methods, as doing so
-    // wouldn't provide any significant performance advantage.
-    return new AbstractCollection()
-    {
-      public int size()
-      {
-        return size;
-      }
-
-      public Iterator iterator()
-      {
-        // Cannot create the iterator directly, because of LinkedHashMap.
-        return HashMap.this.iterator(VALUES);
-      }
-
-      public void clear()
+    if (values == null)
+      // We don't bother overriding many of the optional methods, as doing so
+      // wouldn't provide any significant performance advantage.
+      values = new AbstractCollection()
       {
-        HashMap.this.clear();
-      }
-    };
+        public int size()
+        {
+          return size;
+        }
+
+        public Iterator iterator()
+        {
+          // Cannot create the iterator directly, because of LinkedHashMap.
+          return HashMap.this.iterator(VALUES);
+        }
+
+        public void clear()
+        {
+          HashMap.this.clear();
+        }
+      };
+    return values;
   }
 
   /**
    * Returns a "set view" of this HashMap's entries. The set is backed by
    * the HashMap, so changes in one show up in the other.  The set supports
-   * element removal, but not element addition.
-   * <p>
+   * element removal, but not element addition.<p>
    *
    * Note that the iterators for all three views, from keySet(), entrySet(),
    * and values(), traverse the HashMap in the same sequence.
@@ -564,53 +570,62 @@ public class HashMap extends AbstractMap
    */
   public Set entrySet()
   {
-    // Create an AbstractSet with custom implementations of those methods that
-    // can be overridden easily and efficiently.
-    return new AbstractSet()
-    {
-      public int size()
-      {
-        return size;
-      }
-
-      public Iterator iterator()
-      {
-        // Cannot create the iterator directly, because of LinkedHashMap.
-        return HashMap.this.iterator(ENTRIES);
-      }
-
-      public void clear()
-      {
-        HashMap.this.clear();
-      }
-
-      public boolean contains(Object o)
-      {
-        return getEntry(o) != null;
-      }
-
-      public boolean remove(Object o)
+    if (entries == null)
+      // Create an AbstractSet with custom implementations of those methods
+      // that can be overridden easily and efficiently.
+      entries = new AbstractSet()
       {
-        HashEntry e = getEntry(o);
-        if (e != null)
-          {
-            HashMap.this.remove(e.key);
-            return true;
-          }
-        return false;
-      }
-    };
+        public int size()
+        {
+          return size;
+        }
+
+        public Iterator iterator()
+        {
+          // Cannot create the iterator directly, because of LinkedHashMap.
+          return HashMap.this.iterator(ENTRIES);
+        }
+
+        public void clear()
+        {
+          HashMap.this.clear();
+        }
+
+        public boolean contains(Object o)
+        {
+          return getEntry(o) != null;
+        }
+
+        public boolean remove(Object o)
+        {
+          HashEntry e = getEntry(o);
+          if (e != null)
+            {
+              HashMap.this.remove(e.key);
+              return true;
+            }
+          return false;
+        }
+      };
+    return entries;
   }
 
-  /** Helper method that returns an index in the buckets array for `key;
-   * based on its hashCode().
+  /**
+   * Helper method for put, that creates and adds a new Entry.  This is
+   * overridden in LinkedHashMap for bookkeeping purposes.
    *
-   * @param key the key
-   * @return the bucket number
+   * @param key the key of the new Entry
+   * @param value the value
+   * @param idx the index in buckets where the new Entry belongs
+   * @param callRemove whether to call the removeEldestEntry method
+   * @see #put(Object, Object)
    */
-  int hash(Object key)
+  void addEntry(Object key, Object value, int idx, boolean callRemove)
   {
-    return (key == null) ? 0 : Math.abs(key.hashCode() % buckets.length);
+    HashEntry e = new HashEntry(key, value);
+
+    e.next = buckets[idx];
+    buckets[idx] = e;
   }
 
   /**
@@ -637,6 +652,52 @@ public class HashMap extends AbstractMap
     return null;
   }
 
+  /**
+   * Helper method that returns an index in the buckets array for `key'
+   * based on its hashCode().  Package visible for use by subclasses.
+   *
+   * @param key the key
+   * @return the bucket number
+   */
+  final int hash(Object key)
+  {
+    return key == null ? 0 : Math.abs(key.hashCode() % buckets.length);
+  }
+
+  /**
+   * Generates a parameterized iterator.  Must be overrideable, since
+   * LinkedHashMap iterates in a different order.
+   *
+   * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+   * @return the appropriate iterator
+   */
+  Iterator iterator(int type)
+  {
+    return new HashIterator(type);
+  }
+
+  /**
+   * A simplified, more efficient internal implementation of putAll(). The 
+   * Map constructor and clone() should not call putAll or put, in order to 
+   * be compatible with the JDK implementation with respect to subclasses.
+   *
+   * @param m the map to initialize this from
+   */
+  void putAllInternal(Map m)
+  {
+    Iterator itr = m.entrySet().iterator();
+    int msize = m.size();
+    this.size = msize;
+
+    for (; msize > 0; msize--)
+      {
+       Map.Entry e = (Map.Entry) itr.next();
+       Object key = e.getKey();
+       int idx = hash(key);
+       addEntry(key, e.getValue(), idx, false);
+      }
+  }
+
   /**
    * Increases the size of the HashMap and rehashes all keys to new array
    * indices; this is called when the addition of a new value would cause
@@ -681,35 +742,6 @@ public class HashMap extends AbstractMap
       }
   }
 
-  /**
-   * Generates a parameterized iterator.  Must be overrideable, since
-   * LinkedHashMap iterates in a different order.
-   * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
-   * @return the appropriate iterator
-   */
-  Iterator iterator(int type)
-  {
-    return new HashIterator(type);
-  }
-
-  /**
-   * A simplified, more efficient internal implementation of putAll(). The 
-   * Map constructor and clone() should not call putAll or put, in order to 
-   * be compatible with the JDK implementation with respect to subclasses.
-   */
-  void putAllInternal(Map m)
-  {
-    Iterator itr = m.entrySet().iterator();
-
-    for (int msize = m.size(); msize > 0; msize--)
-      {
-       Map.Entry e = (Map.Entry) itr.next();
-       Object key = e.getKey();
-       int idx = hash(key);
-       addEntry(key, e.getValue(), idx, false);
-      }
-  }
-
   /**
    * Serializes this object to the given stream.
    *
@@ -757,9 +789,6 @@ public class HashMap extends AbstractMap
     // Read and use capacity.
     buckets = new HashEntry[s.readInt()];
     int len = s.readInt();
-    // Already happens automatically.
-    // size = 0;
-    // modCount = 0;
 
     // Read and use key/value pairs.
     for ( ; len > 0; len--)
@@ -773,29 +802,29 @@ public class HashMap extends AbstractMap
    *
    * @author Jon Zeppieri
    */
-  class HashIterator implements Iterator
+  private final class HashIterator implements Iterator
   {
     /**
      * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
      * or {@link #ENTRIES}.
      */
-    final int type;
+    private final int type;
     /**
      * The number of modifications to the backing HashMap that we know about.
      */
-    int knownMod = modCount;
+    private int knownMod = modCount;
     /** The number of elements remaining to be returned by next(). */
-    int count = size;
+    private int count = size;
     /** Current index in the physical hash table. */
-    int idx = buckets.length;
+    private int idx = buckets.length;
     /** The last Entry returned by a next() call. */
-    HashEntry last;
+    private HashEntry last;
     /**
      * The next entry that should be returned by next(). It is set to something
      * if we're iterating through a bucket that contains multiple linked
      * entries. It is null if next() needs to find a new bucket.
      */
-    HashEntry next;
+    private HashEntry next;
 
     /**
      * Construct a new HashIterator with the supplied type.
@@ -840,14 +869,14 @@ public class HashMap extends AbstractMap
       last = e;
       if (type == VALUES)
         return e.value;
-      else if (type == KEYS)
+      if (type == KEYS)
         return e.key;
       return e;
     }
 
     /**
      * Removes from the backing HashMap the last element which was fetched
-     * with the <pre>next()</pre> method.
+     * with the <code>next()</code> method.
      * @throws ConcurrentModificationException if the HashMap was modified
      * @throws IllegalStateException if called when there is no last element
      */
@@ -859,8 +888,8 @@ public class HashMap extends AbstractMap
         throw new IllegalStateException();
 
       HashMap.this.remove(last.key);
-      knownMod++;
       last = null;
+      knownMod++;
     }
   }
 }
index 2b5c31dc7fc64106178ee6d5da43f430b51f50c1..9d9c3315263fd9fa6dea94012c15b6b320dc1383 100644 (file)
@@ -1,5 +1,5 @@
-/* HashSet.java -- a class providing a HashMap-backet Set
-   Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+/* HashSet.java -- a class providing a HashMap-backed Set
+   Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -33,87 +33,115 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 /**
- * This class provides a HashMap-backed implementation of the 
- * Set interface.
- *
- * Each element in the Set is a key in the backing HashMap; each key
- * maps to a static token, denoting that the key does, in fact, exist.
+ * This class provides a HashMap-backed implementation of the Set interface.
+ * <p>
  *
  * Most operations are O(1), assuming no hash collisions.  In the worst
- * case (where all hases collide), operations are O(n).
+ * case (where all hashes collide), operations are O(n). Setting the
+ * initial capacity too low will force many resizing operations, but
+ * setting the initial capacity too high (or loadfactor too low) leads
+ * to wasted memory and slower iteration.
+ * <p>
+ *
+ * HashSet accepts the null key and null values.  It is not synchronized,
+ * so if you need multi-threaded access, consider using:<br>
+ * <code>Set s = Collections.synchronizedSet(new HashSet(...));</code>
+ * <p>
  *
- * HashSet is a part of the JDK1.2 Collections API.
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
  *
- * @author      Jon Zeppieri
+ * @author Jon Zeppieri
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see Set
+ * @see TreeSet
+ * @see Collections#synchronizedSet(Set)
+ * @see HashMap
+ * @see LinkedHashSet
+ * @since 1.2
+ * @status updated to 1.4
  */
 public class HashSet extends AbstractSet
   implements Set, Cloneable, Serializable
 {
-  /** the HashMap which backs this Set */
-  transient HashMap map;
-  static final long serialVersionUID = -5024744406713321676L;
+  /**
+   * Compatible with JDK 1.2.
+   */
+  private static final long serialVersionUID = -5024744406713321676L;
 
   /**
-   * construct a new, empty HashSet whose backing HashMap has the default 
-   * capacity and loadFacor
+   * The HashMap which backs this Set.
+   */
+  private transient HashMap map;
+
+  /**
+   * Construct a new, empty HashSet whose backing HashMap has the default
+   * capacity (11) and loadFacor (0.75).
    */
   public HashSet()
   {
-    map = new HashMap();
+    this(HashMap.DEFAULT_CAPACITY, HashMap.DEFAULT_LOAD_FACTOR);
   }
 
   /**
-   * construct a new, empty HashSet whose backing HashMap has the supplied
-   * capacity and the default load factor
+   * Construct a new, empty HashSet whose backing HashMap has the supplied
+   * capacity and the default load factor (0.75).
    *
-   * @param          initialCapacity          the initial capacity of the backing
-   *                                          HashMap
+   * @param initialCapacity the initial capacity of the backing HashMap
+   * @throws IllegalArgumentException if the capacity is negative
    */
   public HashSet(int initialCapacity)
   {
-    map = new HashMap(initialCapacity);
+    this(initialCapacity, HashMap.DEFAULT_LOAD_FACTOR);
   }
 
   /**
-   * construct a new, empty HashSet whose backing HashMap has the supplied
-   * capacity and load factor
+   * Construct a new, empty HashSet whose backing HashMap has the supplied
+   * capacity and load factor.
    *
-   * @param          initialCapacity          the initial capacity of the backing
-   *                                          HashMap
-   * @param          loadFactor               the load factor of the backing HashMap
+   * @param initialCapacity the initial capacity of the backing HashMap
+   * @param loadFactor the load factor of the backing HashMap
+   * @throws IllegalArgumentException if either argument is negative, or
+   *         if loadFactor is POSITIVE_INFINITY or NaN
    */
   public HashSet(int initialCapacity, float loadFactor)
   {
-    map = new HashMap(initialCapacity, loadFactor);
+    map = init(initialCapacity, loadFactor);
   }
 
   /**
-   * construct a new HashSet with the same elements as are in the supplied
-   * collection (eliminating any duplicates, of course; the backing HashMap
-   * will have the default capacity and load factor
+   * Construct a new HashSet with the same elements as are in the supplied
+   * collection (eliminating any duplicates, of course). The backing storage
+   * has twice the size of the collection, or the default size of 11,
+   * whichever is greater; and the default load factor (0.75).
    *
-   * @param          c          a collection containing the elements with
-   *                            which this set will be initialized
+   * @param c a collection of initial set elements
+   * @throws NullPointerException if c is null
    */
   public HashSet(Collection c)
   {
-    map = new HashMap();
+    this(Math.max(2 * c.size(), HashMap.DEFAULT_CAPACITY));
     addAll(c);
   }
 
   /**
-   * adds the given Object to the set if it is not already in the Set,
-   * returns true if teh element was added, false otherwise
+   * Adds the given Object to the set if it is not already in the Set.
+   * This set permits a null element.
    *
-   * @param       o       the Object to add to this Set
+   * @param o the Object to add to this Set
+   * @return true if the set did not already contain o
    */
   public boolean add(Object o)
   {
-    return (map.put(o, Boolean.TRUE) == null);
+    return map.put(o, "") == null;
   }
 
   /**
-   * empties this Set of all elements; this is a fast operation [O(1)]
+   * Empties this Set of all elements; this takes constant time.
    */
   public void clear()
   {
@@ -121,53 +149,67 @@ public class HashSet extends AbstractSet
   }
 
   /**
-   * returns a shallow copy of this Set (the Set itself is cloned; its 
-   * elements are not)
+   * Returns a shallow copy of this Set. The Set itself is cloned; its
+   * elements are not.
+   *
+   * @return a shallow clone of the set
    */
   public Object clone()
   {
     HashSet copy = null;
     try
       {
-       copy = (HashSet) super.clone();
+        copy = (HashSet) super.clone();
       }
     catch (CloneNotSupportedException x)
       {
+        // Impossible to get here.
       }
     copy.map = (HashMap) map.clone();
     return copy;
   }
 
   /**
-   * returns true if the supplied element is in this Set, false otherwise
+   * Returns true if the supplied element is in this Set.
    *
-   * @param        o         the Object whose presence in this Set we are testing for
+   * @param o the Object to look for
+   * @return true if it is in the set
    */
   public boolean contains(Object o)
   {
     return map.containsKey(o);
   }
 
-  /** 
-   * returns true if this set has no elements in it (size() == 0)
+  /**
+   * Returns true if this set has no elements in it.
+   *
+   * @return <code>size() == 0</code>.
    */
   public boolean isEmpty()
   {
-    return map.isEmpty();
+    return map.size == 0;
   }
 
   /**
-   * returns an Iterator over the elements of this Set; the Iterator allows
-   * removal of elements
+   * Returns an Iterator over the elements of this Set, which visits the
+   * elements in no particular order.  For this class, the Iterator allows
+   * removal of elements. The iterator is fail-fast, and will throw a
+   * ConcurrentModificationException if the set is modified externally.
+   *
+   * @return a set iterator
+   * @see ConcurrentModificationException
    */
   public Iterator iterator()
   {
-    return map.keySet().iterator();
+    // Avoid creating intermediate keySet() object by using non-public API.
+    return map.iterator(HashMap.KEYS);
   }
 
   /**
-   * removes the supplied Object from this Set if it is in the Set; returns
-   * true if an element was removed, false otherwise
+   * Removes the supplied Object from this Set if it is in the Set.
+   *
+   * @param o the object to remove
+   * @return true if an element was removed
    */
   public boolean remove(Object o)
   {
@@ -175,18 +217,42 @@ public class HashSet extends AbstractSet
   }
 
   /**
-   * returns the number of elements in this Set
+   * Returns the number of elements in this Set (its cardinality).
+   *
+   * @return the size of the set
    */
   public int size()
   {
-    return map.size();
+    return map.size;
   }
 
-  /** Serialize this Object in a manner which is binary-compatible with the 
-    * JDK */
+  /**
+   * Helper method which initializes the backing Map. Overridden by
+   * LinkedHashSet for correct semantics.
+   *
+   * @param capacity the initial capacity
+   * @param load the initial load factor
+   * @return the backing HashMap
+   */
+  HashMap init(int capacity, float load)
+  {
+    return new HashMap(capacity, load);
+  }
+
+  /**
+   * Serializes this object to the given stream.
+   *
+   * @param s the stream to write to
+   * @throws IOException if the underlying stream fails
+   * @serialData the <i>capacity</i> (int) and <i>loadFactor</i> (float)
+   *             of the backing store, followed by the set size (int),
+   *             then a listing of its elements (Object) in no order
+   */
   private void writeObject(ObjectOutputStream s) throws IOException
   {
-    Iterator it = iterator();
+    s.defaultWriteObject();
+    // Avoid creating intermediate keySet() object by using non-public API.
+    Iterator it = map.iterator(HashMap.KEYS);
     s.writeInt(map.buckets.length);
     s.writeFloat(map.loadFactor);
     s.writeInt(map.size);
@@ -194,25 +260,23 @@ public class HashSet extends AbstractSet
       s.writeObject(it.next());
   }
 
-  /** Deserialize this Object in a manner which is binary-compatible with 
-    * the JDK */
-  private void readObject(ObjectInputStream s) throws IOException,
-    ClassNotFoundException
+  /**
+   * Deserializes this object from the given stream.
+   *
+   * @param s the stream to read from
+   * @throws ClassNotFoundException if the underlying stream fails
+   * @throws IOException if the underlying stream fails
+   * @serialData the <i>capacity</i> (int) and <i>loadFactor</i> (float)
+   *             of the backing store, followed by the set size (int),
+   *             then a listing of its elements (Object) in no order
+   */
+  private void readObject(ObjectInputStream s)
+    throws IOException, ClassNotFoundException
   {
-    int i, size, capacity;
-    float loadFactor;
-    Object element;
-
-    capacity = s.readInt();
-    loadFactor = s.readFloat();
-    size = s.readInt();
+    s.defaultReadObject();
 
-    map = new HashMap(capacity, loadFactor);
-
-    for (i = 0; i < size; i++)
-      {
-       element = s.readObject();
-       map.put(element, Boolean.TRUE);
-      }
+    map = init(s.readInt(), s.readFloat());
+    for (int size = s.readInt(); size > 0; size--)
+      map.put(s.readObject(), "");
   }
 }
index 2a90244a4d23a9279bdc32e10a9e2dc544d76cbd..01332698c6f958b8e64168dbcb6d6b0f3f84134a 100644 (file)
@@ -66,7 +66,9 @@ import java.io.ObjectOutputStream;
  * Unlike HashMap, Hashtable does not accept `null' as a key value. Also,
  * all accesses are synchronized: in a single thread environment, this is
  * expensive, but in a multi-thread environment, this saves you the effort
- * of extra synchronization.
+ * of extra synchronization. However, the old-style enumerators are not
+ * synchronized, because they can lead to unspecified behavior even if
+ * they were synchronized. You have been warned.
  * <p>
  *
  * The iterators are <i>fail-fast</i>, meaning that any structural
@@ -84,6 +86,7 @@ import java.io.ObjectOutputStream;
  * @see IdentityHashMap
  * @see LinkedHashMap
  * @since 1.0
+ * @status updated to 1.4
  */
 public class Hashtable extends Dictionary
   implements Map, Cloneable, Serializable
@@ -93,6 +96,12 @@ public class Hashtable extends Dictionary
    */
   private static final int DEFAULT_CAPACITY = 11;
 
+  /** An "enum" of iterator types. */
+  // Package visible for use by nested classes.
+  static final int KEYS = 0,
+                   VALUES = 1,
+                   ENTRIES = 2;
+
   /**
    * The default load factor; this is explicitly specified by the spec.
    */
@@ -106,39 +115,57 @@ public class Hashtable extends Dictionary
   /**
    * The rounded product of the capacity and the load factor; when the number
    * of elements exceeds the threshold, the Hashtable calls
-   * <pre>rehash()</pre>.
+   * <code>rehash()</code>.
    * @serial
    */
-  int threshold;
+  private int threshold;
 
   /**
    * Load factor of this Hashtable:  used in computing the threshold.
    * @serial
    */
-  final float loadFactor;
+  private final float loadFactor;
 
   /**
    * Array containing the actual key-value mappings.
    */
+  // Package visible for use by nested classes.
   transient HashEntry[] buckets;
 
   /**
    * Counts the number of modifications this Hashtable has undergone, used
    * by Iterators to know when to throw ConcurrentModificationExceptions.
    */
+  // Package visible for use by nested classes.
   transient int modCount;
 
   /**
    * The size of this Hashtable:  denotes the number of key-value pairs.
    */
+  // Package visible for use by nested classes.
   transient int size;
 
+  /**
+   * The cache for {@link #keySet()}.
+   */
+  private transient Set keys;
+
+  /**
+   * The cache for {@link #values()}.
+   */
+  private transient Collection values;
+
+  /**
+   * The cache for {@link #entrySet()}.
+   */
+  private transient Set entries;
+
   /**
    * Class to represent an entry in the hash table. Holds a single key-value
    * pair. A Hashtable Entry is identical to a HashMap Entry, except that
    * `null' is not allowed for keys and values.
    */
-  static class HashEntry extends BasicMapEntry
+  private static final class HashEntry extends BasicMapEntry
   {
     /** The next entry in the linked list. */
     HashEntry next;
@@ -159,7 +186,7 @@ public class Hashtable extends Dictionary
      * @return the prior value
      * @throws NullPointerException if <code>newVal</code> is null
      */
-    public final Object setValue(Object newVal)
+    public Object setValue(Object newVal)
     {
       if (newVal == null)
         throw new NullPointerException();
@@ -193,15 +220,15 @@ public class Hashtable extends Dictionary
   public Hashtable(Map m)
   {
     this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
-    putAll(m);
+    putAllInternal(m);
   }
 
   /**
    * Construct a new Hashtable with a specific inital capacity and
    * default load factor of 0.75.
    *
-   * @param initialCapacity the initial capacity of this Hashtable (>=0)
-   * @throws IllegalArgumentException if (initialCapacity < 0)
+   * @param initialCapacity the initial capacity of this Hashtable (&gt;= 0)
+   * @throws IllegalArgumentException if (initialCapacity &lt; 0)
    */
   public Hashtable(int initialCapacity)
   {
@@ -212,10 +239,10 @@ public class Hashtable extends Dictionary
    * Construct a new Hashtable with a specific initial capacity and
    * load factor.
    *
-   * @param initialCapacity the initial capacity (>=0)
-   * @param loadFactor the load factor (>0, not NaN)
-   * @throws IllegalArgumentException if (initialCapacity < 0) ||
-   *                                     ! (loadFactor > 0.0)
+   * @param initialCapacity the initial capacity (&gt;= 0)
+   * @param loadFactor the load factor (&gt; 0, not NaN)
+   * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
+   *                                     ! (loadFactor &gt; 0.0)
    */
   public Hashtable(int initialCapacity, float loadFactor)
   {
@@ -251,30 +278,36 @@ public class Hashtable extends Dictionary
   }
 
   /**
-   * Return an enumeration of the keys of this table.
+   * Return an enumeration of the keys of this table. There's no point
+   * in synchronizing this, as you have already been warned that the
+   * enumeration is not specified to be thread-safe.
+   *
    * @return the keys
    * @see #elements()
    * @see #keySet()
    */
-  public synchronized Enumeration keys()
+  public Enumeration keys()
   {
-    return new Enumerator(Enumerator.KEYS);
+    return new Enumerator(KEYS);
   }
 
   /**
-   * Return an enumeration of the values of this table.
+   * Return an enumeration of the values of this table. There's no point
+   * in synchronizing this, as you have already been warned that the
+   * enumeration is not specified to be thread-safe.
+   *
    * @return the values
    * @see #keys()
    * @see #values()
    */
-  public synchronized Enumeration elements()
+  public Enumeration elements()
   {
-    return new Enumerator(Enumerator.VALUES);
+    return new Enumerator(VALUES);
   }
 
   /**
-   * Returns true if this Hashtable contains a value <pre>o</pre>,
-   * such that <pre>o.equals(value)</pre>.  This is the same as
+   * Returns true if this Hashtable contains a value <code>o</code>,
+   * such that <code>o.equals(value)</code>.  This is the same as
    * <code>containsValue()</code>, and is O(n).
    * <p>
    *
@@ -284,48 +317,46 @@ public class Hashtable extends Dictionary
    *
    * @param value the value to search for in this Hashtable
    * @return true if at least one key maps to the value
-   * @throws NullPointerException if <pre>value</pre> is null
+   * @throws NullPointerException if <code>value</code> is null
    * @see #containsValue(Object)
    * @see #containsKey(Object)
    */
   public synchronized boolean contains(Object value)
   {
-    // Check if value is null in case Hashtable is empty.
+    // Check if value is null.
     if (value == null)
       throw new NullPointerException();
-
-    for (int i = buckets.length - 1; i >= 0; i--)
-      {
-        HashEntry e = buckets[i];
-        while (e != null)
-          {
-            if (value.equals(e.value))
-              return true;
-            e = e.next;
-          }
-      }
-    return false;
+    return containsValue(value);
   }
 
   /**
-   * Returns true if this Hashtable contains a value <pre>o</pre>, such that
-   * <pre>o.equals(value)</pre>. This is the new API for the old
-   * <code>contains()</code>.
+   * Returns true if this Hashtable contains a value <code>o</code>, such that
+   * <code>o.equals(value)</code>. This is the new API for the old
+   * <code>contains()</code>, except that it is forgiving of null.
    *
    * @param value the value to search for in this Hashtable
    * @return true if at least one key maps to the value
-   * @throws NullPointerException if <pre>value</pre> is null
    * @see #contains(Object)
    * @see #containsKey(Object)
    * @since 1.2
    */
   public boolean containsValue(Object value)
   {
-    return contains(value);
+    for (int i = buckets.length - 1; i >= 0; i--)
+      {
+        HashEntry e = buckets[i];
+        while (e != null)
+          {
+            if (AbstractCollection.equals(value, e.value))
+              return true;
+            e = e.next;
+          }
+      }
+    return false;
   }
 
   /**
-   * Returns true if the supplied object <pre>equals()</pre> a key
+   * Returns true if the supplied object <code>equals()</code> a key
    * in this Hashtable.
    *
    * @param key the key to search for in this Hashtable
@@ -348,7 +379,7 @@ public class Hashtable extends Dictionary
 
   /**
    * Return the value in this Hashtable associated with the supplied key,
-   * or <pre>null</pre> if the key maps to nothing.
+   * or <code>null</code> if the key maps to nothing.
    *
    * @param key the key for which to fetch an associated value
    * @return what the key maps to, if present
@@ -383,7 +414,6 @@ public class Hashtable extends Dictionary
    */
   public synchronized Object put(Object key, Object value)
   {
-    modCount++;
     int idx = hash(key);
     HashEntry e = buckets[idx];
 
@@ -407,6 +437,7 @@ public class Hashtable extends Dictionary
       }
 
     // At this point, we know we need to add a new entry.
+    modCount++;
     if (++size > threshold)
       {
         rehash();
@@ -425,15 +456,18 @@ public class Hashtable extends Dictionary
   /**
    * Removes from the table and returns the value which is mapped by the
    * supplied key. If the key maps to nothing, then the table remains
-   * unchanged, and <pre>null</pre> is returned.
+   * unchanged, and <code>null</code> is returned.
+   * <b>NOTE:</b>Map.remove and Dictionary.remove disagree whether null
+   * is a valid parameter; at the moment, this implementation obeys Map.remove,
+   * and silently ignores null.
    *
    * @param key the key used to locate the value to remove
    * @return whatever the key mapped to, if present
-   * @throws NullPointerException if key is null
    */
   public synchronized Object remove(Object key)
   {
-    modCount++;
+    if (key == null)
+      return null;
     int idx = hash(key);
     HashEntry e = buckets[idx];
     HashEntry last = null;
@@ -442,6 +476,7 @@ public class Hashtable extends Dictionary
       {
         if (key.equals(e.key))
           {
+            modCount++;
             if (last == null)
               buckets[idx] = e.next;
             else
@@ -488,9 +523,12 @@ public class Hashtable extends Dictionary
    */
   public synchronized void clear()
   {
-    modCount++;
-    Arrays.fill(buckets, null);
-    size = 0;
+    if (size > 0)
+      {
+        modCount++;
+        Arrays.fill(buckets, null);
+        size = 0;
+      }
   }
 
   /**
@@ -511,36 +549,18 @@ public class Hashtable extends Dictionary
         // This is impossible.
       }
     copy.buckets = new HashEntry[buckets.length];
-
-    for (int i = buckets.length - 1; i >= 0; i--)
-      {
-        HashEntry e = buckets[i];
-        HashEntry last = null;
-
-        while (e != null)
-          {
-            if (last == null)
-              {
-                last = new HashEntry(e.key, e.value);
-                copy.buckets[i] = last;
-              }
-            else
-              {
-                last.next = new HashEntry(e.key, e.value);
-                last = last.next;
-              }
-            e = e.next;
-          }
-      }
+    copy.putAllInternal(this);
+    // Clear the caches.
+    copy.keys = null;
+    copy.values = null;
+    copy.entries = null;
     return copy;
   }
 
   /**
-   * Converts this Hashtable to a String, surrounded by braces (<pre>'{'</pre>
-   * and <pre>'}'</pre>), key/value pairs listed with an equals sign between,
-   * (<pre>'='</pre>), and pairs separated by comma and space
-   * (<pre>", "</pre>).
-   * <p>
+   * Converts this Hashtable to a String, surrounded by braces, and with
+   * key/value pairs listed with an equals sign between, separated by a
+   * comma and space. For example, <code>"{a=1, b=2}"</code>.<p>
    *
    * NOTE: if the <code>toString()</code> method of any key or value
    * throws an exception, this will fail for the same reason.
@@ -552,7 +572,7 @@ public class Hashtable extends Dictionary
     // Since we are already synchronized, and entrySet().iterator()
     // would repeatedly re-lock/release the monitor, we directly use the
     // unsynchronized HashIterator instead.
-    Iterator entries = new HashIterator(HashIterator.ENTRIES);
+    Iterator entries = new HashIterator(ENTRIES);
     StringBuffer r = new StringBuffer("{");
     for (int pos = size; pos > 0; pos--)
       {
@@ -568,9 +588,11 @@ public class Hashtable extends Dictionary
    * Returns a "set view" of this Hashtable's keys. The set is backed by
    * the hashtable, so changes in one show up in the other.  The set supports
    * element removal, but not element addition.  The set is properly
-   * synchronized on the original hashtable.  The set will throw a
-   * {@link NullPointerException} if null is passed to <code>contains</code>,
-   * <code>remove</code>, or related methods.
+   * synchronized on the original hashtable.  Sun has not documented the
+   * proper interaction of null with this set, but has inconsistent behavior
+   * in the JDK. Therefore, in this implementation, contains, remove,
+   * containsAll, retainAll, removeAll, and equals just ignore a null key
+   * rather than throwing a {@link NullPointerException}.
    *
    * @return a set view of the keys
    * @see #values()
@@ -579,50 +601,56 @@ public class Hashtable extends Dictionary
    */
   public Set keySet()
   {
-    // Create a synchronized AbstractSet with custom implementations of those
-    // methods that can be overridden easily and efficiently.
-    Set r = new AbstractSet()
-    {
-      public int size()
+    if (keys == null)
       {
-        return size;
-      }
+        // Create a synchronized AbstractSet with custom implementations of
+        // those methods that can be overridden easily and efficiently.
+        Set r = new AbstractSet()
+        {
+          public int size()
+          {
+            return size;
+          }
 
-      public Iterator iterator()
-      {
-        return new HashIterator(HashIterator.KEYS);
-      }
+          public Iterator iterator()
+          {
+            return new HashIterator(KEYS);
+          }
 
-      public void clear()
-      {
-        Hashtable.this.clear();
-      }
+          public void clear()
+          {
+            Hashtable.this.clear();
+          }
 
-      public boolean contains(Object o)
-      {
-        return Hashtable.this.containsKey(o);
-      }
+          public boolean contains(Object o)
+          {
+            if (o == null)
+              return false;
+            return containsKey(o);
+          }
 
-      public boolean remove(Object o)
-      {
-        return (Hashtable.this.remove(o) != null);
+          public boolean remove(Object o)
+          {
+            return Hashtable.this.remove(o) != null;
+          }
+        };
+        // We must specify the correct object to synchronize upon, hence the
+        // use of a non-public API
+        keys = new Collections.SynchronizedSet(this, r);
       }
-    };
-
-    // We must specify the correct object to synchronize upon, hence the
-    // use of a non-public API
-    return new Collections.SynchronizedSet(this, r);
+    return keys;
   }
 
-
   /**
    * Returns a "collection view" (or "bag view") of this Hashtable's values.
    * The collection is backed by the hashtable, so changes in one show up
    * in the other.  The collection supports element removal, but not element
    * addition.  The collection is properly synchronized on the original
-   * hashtable.  The collection will throw a {@link NullPointerException}
-   * if null is passed to <code>contains</code> or related methods, but not
-   * if passed to <code>remove</code> or related methods.
+   * hashtable.  Sun has not documented the proper interaction of null with
+   * this set, but has inconsistent behavior in the JDK. Therefore, in this
+   * implementation, contains, remove, containsAll, retainAll, removeAll, and
+   * equals just ignore a null value rather than throwing a
+   * {@link NullPointerException}.
    *
    * @return a bag view of the values
    * @see #keySet()
@@ -631,46 +659,45 @@ public class Hashtable extends Dictionary
    */
   public Collection values()
   {
-    // We don't bother overriding many of the optional methods, as doing so
-    // wouldn't provide any significant performance advantage.
-    Collection r = new AbstractCollection()
-    {
-      public int size()
-      {
-        return size;
-      }
-
-      public Iterator iterator()
+    if (values == null)
       {
-        return new HashIterator(HashIterator.VALUES);
-      }
+        // We don't bother overriding many of the optional methods, as doing so
+        // wouldn't provide any significant performance advantage.
+        Collection r = new AbstractCollection()
+        {
+          public int size()
+          {
+            return size;
+          }
 
-      public void clear()
-      {
-        Hashtable.this.clear();
-      }
+          public Iterator iterator()
+          {
+            return new HashIterator(VALUES);
+          }
 
-      // Override this so that we check for null
-      public boolean contains(Object o)
-      {
-        return Hashtable.this.contains(o);
+          public void clear()
+          {
+            Hashtable.this.clear();
+          }
+        };
+        // We must specify the correct object to synchronize upon, hence the
+        // use of a non-public API
+        values = new Collections.SynchronizedCollection(this, r);
       }
-    };
-
-    // We must specify the correct object to synchronize upon, hence the
-    // use of a non-public API
-    return new Collections.SynchronizedCollection(this, r);
+    return values;
   }
 
   /**
    * Returns a "set view" of this Hashtable's entries. The set is backed by
    * the hashtable, so changes in one show up in the other.  The set supports
    * element removal, but not element addition.  The set is properly
-   * synchronized on the original hashtable.  The set will throw a
-   * {@link NullPointerException} if the Map.Entry passed to
-   * <code>contains</code>, <code>remove</code>, or related methods returns
-   * null for <code>getKey</code>, but not if the Map.Entry is null or
-   * returns null for <code>getValue</code>.
+   * synchronized on the original hashtable.  Sun has not documented the
+   * proper interaction of null with this set, but has inconsistent behavior
+   * in the JDK. Therefore, in this implementation, contains, remove,
+   * containsAll, retainAll, removeAll, and equals just ignore a null entry,
+   * or an entry with a null key or value, rather than throwing a
+   * {@link NullPointerException}. However, calling entry.setValue(null)
+   * will fail.
    * <p>
    *
    * Note that the iterators for all three views, from keySet(), entrySet(),
@@ -684,49 +711,52 @@ public class Hashtable extends Dictionary
    */
   public Set entrySet()
   {
-    // Create an AbstractSet with custom implementations of those methods that
-    // can be overridden easily and efficiently.
-    Set r = new AbstractSet()
-    {
-      public int size()
+    if (entries == null)
       {
-        return size;
-      }
+        // Create an AbstractSet with custom implementations of those methods
+        // that can be overridden easily and efficiently.
+        Set r = new AbstractSet()
+        {
+          public int size()
+          {
+            return size;
+          }
 
-      public Iterator iterator()
-      {
-        return new HashIterator(HashIterator.ENTRIES);
-      }
+          public Iterator iterator()
+          {
+            return new HashIterator(ENTRIES);
+          }
 
-      public void clear()
-      {
-        Hashtable.this.clear();
-      }
+          public void clear()
+          {
+            Hashtable.this.clear();
+          }
 
-      public boolean contains(Object o)
-      {
-        return getEntry(o) != null;
-      }
+          public boolean contains(Object o)
+          {
+            return getEntry(o) != null;
+          }
 
-      public boolean remove(Object o)
-      {
-        HashEntry e = getEntry(o);
-        if (e != null)
+          public boolean remove(Object o)
           {
-            Hashtable.this.remove(e.key);
-            return true;
+            HashEntry e = getEntry(o);
+            if (e != null)
+              {
+                Hashtable.this.remove(e.key);
+                return true;
+              }
+            return false;
           }
-        return false;
+        };
+        // We must specify the correct object to synchronize upon, hence the
+        // use of a non-public API
+        entries = new Collections.SynchronizedSet(this, r);
       }
-    };
-
-    // We must specify the correct object to synchronize upon, hence the
-    // use of a non-public API
-    return new Collections.SynchronizedSet(this, r);
+    return entries;
   }
 
   /**
-   * Returns true if this Hashtable equals the supplied Object <pre>o</pre>.
+   * Returns true if this Hashtable equals the supplied Object <code>o</code>.
    * As specified by Map, this is:
    * <pre>
    * (o instanceof Map) && entrySet().equals(((Map) o).entrySet());
@@ -759,7 +789,7 @@ public class Hashtable extends Dictionary
     // Since we are already synchronized, and entrySet().iterator()
     // would repeatedly re-lock/release the monitor, we directly use the
     // unsynchronized HashIterator instead.
-    Iterator itr = new HashIterator(HashIterator.ENTRIES);
+    Iterator itr = new HashIterator(ENTRIES);
     int hashcode = 0;
     for (int pos = size; pos > 0; pos--)
       hashcode += itr.next().hashCode();
@@ -782,29 +812,55 @@ public class Hashtable extends Dictionary
 
   /**
    * Helper method for entrySet(), which matches both key and value
-   * simultaneously.
+   * simultaneously. Ignores null, as mentioned in entrySet().
    *
    * @param o the entry to match
    * @return the matching entry, if found, or null
-   * @throws NullPointerException if me.getKey() returns null
    * @see #entrySet()
    */
   private HashEntry getEntry(Object o)
   {
-    if (!(o instanceof Map.Entry))
+    if (! (o instanceof Map.Entry))
+      return null;
+    Object key = ((Map.Entry) o).getKey();
+    if (key == null)
       return null;
-    Map.Entry me = (Map.Entry) o;
-    int idx = hash(me.getKey());
+
+    int idx = hash(key);
     HashEntry e = buckets[idx];
     while (e != null)
       {
-        if (e.equals(me))
+        if (o.equals(e))
           return e;
         e = e.next;
       }
     return null;
   }
 
+  /**
+   * A simplified, more efficient internal implementation of putAll(). The 
+   * Map constructor and clone() should not call putAll or put, in order to 
+   * be compatible with the JDK implementation with respect to subclasses.
+   *
+   * @param m the map to initialize this from
+   */
+  void putAllInternal(Map m)
+  {
+    Iterator itr = m.entrySet().iterator();
+    int msize = m.size();
+    this.size = msize;
+
+    for (; msize > 0; msize--)
+      {
+       Map.Entry e = (Map.Entry) itr.next();
+       Object key = e.getKey();
+       int idx = hash(key);
+       HashEntry he = new HashEntry(key, e.getValue());
+       he.next = buckets[idx];
+       buckets[idx] = he;
+      }
+  }
+
   /**
    * Increases the size of the Hashtable and rehashes all keys to new array
    * indices; this is called when the addition of a new value would cause
@@ -813,7 +869,8 @@ public class Hashtable extends Dictionary
    * <p>
    *
    * This is not specified, but the new size is twice the current size plus
-   * one; this number is not always prime, unfortunately.
+   * one; this number is not always prime, unfortunately. This implementation
+   * is not synchronized, as it is only invoked from synchronized methods.
    */
   protected void rehash()
   {
@@ -854,8 +911,8 @@ public class Hashtable extends Dictionary
    *
    * @param s the stream to write to
    * @throws IOException if the underlying stream fails
-   * @serialData the <i>capacity</i>(int) that is the length of the
-   *             bucket array, the <i>size</i>(int) of the hash map
+   * @serialData the <i>capacity</i> (int) that is the length of the
+   *             bucket array, the <i>size</i> (int) of the hash map
    *             are emitted first.  They are followed by size entries,
    *             each consisting of a key (Object) and a value (Object).
    */
@@ -870,7 +927,7 @@ public class Hashtable extends Dictionary
     // Since we are already synchronized, and entrySet().iterator()
     // would repeatedly re-lock/release the monitor, we directly use the
     // unsynchronized HashIterator instead.
-    Iterator it = new HashIterator(HashIterator.ENTRIES);
+    Iterator it = new HashIterator(ENTRIES);
     while (it.hasNext())
       {
         HashEntry entry = (HashEntry) it.next();
@@ -885,8 +942,8 @@ public class Hashtable extends Dictionary
    * @param s the stream to read from
    * @throws ClassNotFoundException if the underlying stream fails
    * @throws IOException if the underlying stream fails
-   * @serialData the <i>capacity</i>(int) that is the length of the
-   *             bucket array, the <i>size</i>(int) of the hash map
+   * @serialData the <i>capacity</i> (int) that is the length of the
+   *             bucket array, the <i>size</i> (int) of the hash map
    *             are emitted first.  They are followed by size entries,
    *             each consisting of a key (Object) and a value (Object).
    */
@@ -901,7 +958,8 @@ public class Hashtable extends Dictionary
     int len = s.readInt();
 
     // Read and use key/value pairs.
-    for ( ; len > 0; len--)
+    // TODO: should we be defensive programmers, and check for illegal nulls?
+    while (--len >= 0)
       put(s.readObject(), s.readObject());
   }
 
@@ -916,13 +974,8 @@ public class Hashtable extends Dictionary
    *
    * @author Jon Zeppieri
    */
-  class HashIterator implements Iterator
+  private final class HashIterator implements Iterator
   {
-    /** "enum" of iterator types. */
-    static final int KEYS = 0,
-                     VALUES = 1,
-                     ENTRIES = 2;
-
     /**
      * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
      * or {@link #ENTRIES}.
@@ -988,14 +1041,14 @@ public class Hashtable extends Dictionary
       last = e;
       if (type == VALUES)
         return e.value;
-      else if (type == KEYS)
+      if (type == KEYS)
         return e.key;
       return e;
     }
 
     /**
      * Removes from the backing Hashtable the last element which was fetched
-     * with the <pre>next()</pre> method.
+     * with the <code>next()</code> method.
      * @throws ConcurrentModificationException if the hashtable was modified
      * @throws IllegalStateException if called when there is no last element
      */
@@ -1007,10 +1060,10 @@ public class Hashtable extends Dictionary
         throw new IllegalStateException();
 
       Hashtable.this.remove(last.key);
-      knownMod++;
       last = null;
+      knownMod++;
     }
-  }
+  } // class HashIterator
 
 
   /**
@@ -1027,21 +1080,21 @@ public class Hashtable extends Dictionary
    *
    * @author Jon Zeppieri
    */
-  class Enumerator implements Enumeration
+  private final class Enumerator implements Enumeration
   {
-    /** "enum" of iterator types. */
-    static final int KEYS = 0,
-                     VALUES = 1;
-
     /**
      * The type of this Iterator: {@link #KEYS} or {@link #VALUES}.
      */
-    int type;
+    final int type;
+    /** The number of elements remaining to be returned by next(). */
+    int count = size;
     /** Current index in the physical hash table. */
-    int idx;
-    /** The last Entry returned by nextEntry(). */
-    HashEntry last;
-    /** Entry which will be returned by the next nextElement() call. */
+    int idx = buckets.length;
+    /**
+     * Entry which will be returned by the next nextElement() call. It is
+     * set if we are iterating through a bucket with multiple entries, or null
+     * if we must look in the next bucket.
+     */
     HashEntry next;
 
     /**
@@ -1051,25 +1104,6 @@ public class Hashtable extends Dictionary
     Enumerator(int type)
     {
       this.type = type;
-      this.idx = buckets.length;
-    }
-
-    /**
-     * Helper method to find the next entry.
-     * @return the next entry, or null
-     */
-    private HashEntry nextEntry()
-    {
-      HashEntry e = null;
-
-      if (last != null)
-        e = last.next;
-
-      while (e == null && idx > 0)
-        e = buckets[--idx];
-
-      last = e;
-      return e;
     }
 
     /**
@@ -1078,10 +1112,7 @@ public class Hashtable extends Dictionary
      */
     public boolean hasMoreElements()
     {
-      if (next != null)
-        return true;
-      next = nextEntry();
-      return next != null;
+      return count > 0;
     }
 
     /**
@@ -1091,19 +1122,16 @@ public class Hashtable extends Dictionary
      */
     public Object nextElement()
     {
-      HashEntry e;
-      if (next != null)
-        {
-          e = next;
-          next = null;
-        }
-      else
-        e = nextEntry();
-      if (e == null)
+      if (count == 0)
         throw new NoSuchElementException("Hashtable Enumerator");
-      if (type == VALUES)
-        return e.value;
-      return e.key;
+      count--;
+      HashEntry e = next;
+
+      while (e == null)
+        e = buckets[--idx];
+
+      next = e.next;
+      return type == VALUES ? e.value : e.key;
     }
-  }
+  } // class Enumerator
 }
index da028ed9c02452d59afef1d1676ca62a49a8e6aa..d6a2f7c7c2d6ee159d8ab405b0e17356e8b96568 100644 (file)
@@ -31,403 +31,888 @@ import java.io.*;
 
 /**
  * This class provides a hashtable-backed implementation of the
- * Map interface.  Unlike HashMap, it uses object identity to
- * do its hashing.  Also, it uses a linear-probe hash table.
+ * Map interface, but uses object identity to do its hashing.  In fact,
+ * it uses object identity for comparing values, as well. It uses a
+ * linear-probe hash table, which may have faster performance
+ * than the chaining employed by HashMap.
+ * <p>
+ *
+ * <em>WARNING: This is not a general purpose map. Because it uses
+ * System.identityHashCode and ==, instead of hashCode and equals, for
+ * comparison, it violated Map's general contract, and may cause
+ * undefined behavior when compared to other maps which are not
+ * IdentityHashMaps.  This is designed only for the rare cases when
+ * identity semantics are needed.</em> An example use is
+ * topology-preserving graph transformations, such as deep cloning,
+ * or as proxy object mapping such as in debugging.
+ * <p>
+ *
+ * This map permits <code>null</code> keys and values, and does not
+ * guarantee that elements will stay in the same order over time. The
+ * basic operations (<code>get</code> and <code>put</code>) take
+ * constant time, provided System.identityHashCode is decent. You can
+ * tune the behavior by specifying the expected maximum size. As more
+ * elements are added, the map may need to allocate a larger table,
+ * which can be expensive.
+ * <p>
+ *
+ * This implementation is unsynchronized.  If you want multi-thread
+ * access to be consistent, you must synchronize it, perhaps by using
+ * <code>Collections.synchronizedMap(new IdentityHashMap(...));</code>.
+ * The iterators are <i>fail-fast</i>, meaning that a structural modification
+ * made to the map outside of an iterator's remove method cause the
+ * iterator, and in the case of the entrySet, the Map.Entry, to
+ * fail with a {@link ConcurrentModificationException}.
  *
  * @author Tom Tromey <tromey@redhat.com>
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see System#identityHashCode(Object)
+ * @see Collection
+ * @see Map
+ * @see HashMap
+ * @see TreeMap
+ * @see LinkedHashMap
+ * @see WeakHashMap
  * @since 1.4
+ * @status updated to 1.4
  */
 public class IdentityHashMap extends AbstractMap
   implements Map, Serializable, Cloneable
 {
+  /** The default capacity. */
   private static final int DEFAULT_CAPACITY = 21;
 
-  /** Create a new IdentityHashMap with the default capacity (21
-   * entries).
+  /**
+   * This object is used to mark deleted items. Package visible for use by
+   * nested classes.
+   */
+  static final Object tombstone = new Object();
+
+  /**
+   * This object is used to mark empty slots.  We need this because
+   * using null is ambiguous. Package visible for use by nested classes.
+   */
+  static final Object emptyslot = new Object();
+
+  /**
+   * Compatible with JDK 1.4.
+   */
+  private static final long serialVersionUID = 8188218128353913216L;
+
+  /**
+   * The number of mappings in the table. Package visible for use by nested
+   * classes.
+   * @serial
+   */
+  int size;
+
+  /**
+   * The table itself. Package visible for use by nested classes.
    */
-  public IdentityHashMap ()
+  transient Object[] table;
+
+  /**
+   * The number of structural modifications made so far. Package visible for
+   * use by nested classes.
+   */
+  transient int modCount;
+
+  /**
+   * The cache for {@link #entrySet()}.
+   */
+  private transient Set entries;
+
+  /**
+   * The threshold for rehashing, which is 75% of (table.length / 2).
+   */
+  private transient int threshold;
+
+  /**
+   * Create a new IdentityHashMap with the default capacity (21 entries).
+   */
+  public IdentityHashMap()
   {
-    this (DEFAULT_CAPACITY);
+    this(DEFAULT_CAPACITY);
   }
 
-  /** Create a new IdentityHashMap with the indicated number of
+  /**
+   * Create a new IdentityHashMap with the indicated number of
    * entries.  If the number of elements added to this hash map
    * exceeds this maximum, the map will grow itself; however, that
    * incurs a performance penalty.
-   * @param max Initial size
+   *
+   * @param max initial size
+   * @throws IllegalArgumentException if max is negative
    */
-  public IdentityHashMap (int max)
+  public IdentityHashMap(int max)
   {
     if (max < 0)
-      throw new IllegalArgumentException ();
+      throw new IllegalArgumentException();
+    // Need at least two slots, or hash() will break.
+    if (max < 2)
+      max = 2;
     table = new Object[2 * max];
-    Arrays.fill (table, emptyslot);
-    size = 0;
+    Arrays.fill(table, emptyslot);
+    // This is automatically set.
+    // size = 0;
+    threshold = max / 4 * 3;
   }
 
-  /** Create a new IdentityHashMap whose contents are taken from the
+  /**
+   * Create a new IdentityHashMap whose contents are taken from the
    * given Map.
-   * @param m The map whose elements are to be put in this map.
+   *
+   * @param m The map whose elements are to be put in this map
+   * @throws NullPointerException if m is null
    */
-  public IdentityHashMap (Map m)
+  public IdentityHashMap(Map m)
   {
-    int len = 2 * Math.max (m.size (), DEFAULT_CAPACITY);
-    table = new Object[len];
-    Arrays.fill (table, emptyslot);
-    putAll (m);
+    this(Math.max(m.size() * 2, DEFAULT_CAPACITY));
+    putAll(m);
   }
 
-  public void clear ()
+  /**
+   * Remove all mappings from this map.
+   */
+  public void clear()
   {
-    Arrays.fill (table, emptyslot);
-    size = 0;
+    if (size != 0)
+      {
+        modCount++;
+        Arrays.fill(table, emptyslot);
+        size = 0;
+      }
   }
 
   /**
    * Creates a shallow copy where keys and values are not cloned.
    */
-  public Object clone ()
+  public Object clone()
   {
-    try 
+    try
       {
-        IdentityHashMap copy = (IdentityHashMap) super.clone ();
-       copy.table = (Object[]) table.clone ();
-       return copy;
+        IdentityHashMap copy = (IdentityHashMap) super.clone();
+        copy.table = (Object[]) table.clone();
+        copy.entries = null; // invalidate the cache
+        return copy;
       }
-    catch (CloneNotSupportedException e) 
+    catch (CloneNotSupportedException e)
       {
-       // Can't happen.
-       return null;
+        // Can't happen.
+        return null;
       }
   }
 
-  public boolean containsKey (Object key)
+  /**
+   * Tests whether the specified key is in this map.  Unlike normal Maps,
+   * this test uses <code>entry == key</code> instead of
+   * <code>entry == null ? key == null : entry.equals(key)</code>.
+   *
+   * @param key the key to look for
+   * @return true if the key is contained in the map
+   * @see #containsValue(Object)
+   * @see #get(Object)
+   */
+  public boolean containsKey(Object key)
   {
-    int h = getHash (key);
-    int save = h;
-    while (true)
-      {
-       if (table[h] == key)
-         return true;
-       if (table[h] == emptyslot)
-         return false;
-       h += 2;
-       if (h >= table.length)
-         h = 0;
-       if (h == save)
-         return false;
-      }
+    return key == table[hash(key)];
   }
 
-  public boolean containsValue (Object value)
+  /**
+   * Returns true if this HashMap contains the value.  Unlike normal maps,
+   * this test uses <code>entry == value</code> instead of
+   * <code>entry == null ? value == null : entry.equals(value)</code>.
+   *
+   * @param value the value to search for in this HashMap
+   * @return true if at least one key maps to the value
+   * @see #containsKey(Object)
+   */
+  public boolean containsValue(Object value)
   {
-    for (int i = 1; i < table.length; i += 2)
+    for (int i = table.length - 1; i > 0; i -= 2)
       if (table[i] == value)
-       return true;
+        return true;
     return false;
   }
 
-  public Set entrySet ()
+  /**
+   * Returns a "set view" of this Map's entries. The set is backed by
+   * the Map, so changes in one show up in the other.  The set supports
+   * element removal, but not element addition.
+   * <p>
+   *
+   * <em>The semantics of this set, and of its contained entries, are
+   * different from the contract of Set and Map.Entry in order to make
+   * IdentityHashMap work.  This means that while you can compare these
+   * objects between IdentityHashMaps, comparing them with regular sets
+   * or entries is likely to have undefined behavior.</em>  The entries
+   * in this set are reference-based, rather than the normal object
+   * equality.  Therefore, <code>e1.equals(e2)</code> returns
+   * <code>e1.getKey() == e2.getKey() && e1.getValue() == e2.getValue()</code>,
+   * and <code>e.hashCode()</code> returns
+   * <code>System.identityHashCode(e.getKey()) ^
+   *       System.identityHashCode(e.getValue())</code>.
+   * <p>
+   *
+   * Note that the iterators for all three views, from keySet(), entrySet(),
+   * and values(), traverse the Map in the same sequence.
+   *
+   * @return a set view of the entries
+   * @see #keySet()
+   * @see #values()
+   * @see Map.Entry
+   */
+  public Set entrySet()
   {
-    return new AbstractSet ()
-    {
-      public int size ()
+    if (entries == null)
+      entries = new AbstractSet()
       {
-       return size;
-      }
-
-      public Iterator iterator ()
-      {
-       return new IdentityIterator (IdentityIterator.ENTRIES);
-      }
-
-      public void clear ()
-      {
-       IdentityHashMap.this.clear ();
-      }
+        public int size()
+        {
+          return size;
+        }
+
+        public Iterator iterator()
+        {
+          return new IdentityIterator(ENTRIES);
+        }
+
+        public void clear()
+        {
+          IdentityHashMap.this.clear();
+        }
+
+        public boolean contains(Object o)
+        {
+          if (! (o instanceof Map.Entry))
+            return false;
+          Map.Entry m = (Map.Entry) o;
+          return m.getValue() == table[hash(m.getKey()) + 1];
+        }
+
+        public int hashCode()
+        {
+          return IdentityHashMap.this.hashCode();
+        }
+
+        public boolean remove(Object o)
+        {
+          if (! (o instanceof Map.Entry))
+            return false;
+          Object key = ((Map.Entry) o).getKey();
+          int h = hash(key);
+          if (table[h] == key)
+            {
+              size--;
+              modCount++;
+              table[h] = tombstone;
+              table[h + 1] = tombstone;
+              return true;
+            }
+          return false;
+        }
+      };
+    return entries;
+  }
 
-      public boolean contains (Object o)
-      {
-       if (! (o instanceof Map.Entry))
-         return false;
-       Map.Entry m = (Map.Entry) o;
-       return (IdentityHashMap.this.containsKey (m.getKey ())
-               && IdentityHashMap.this.get (m.getKey ()) == m.getValue ());
-      }
+  /**
+   * Compares two maps for equality. This returns true only if both maps
+   * have the same reference-identity comparisons. While this returns
+   * <code>this.entrySet().equals(m.entrySet())</code> as specified by Map,
+   * this will not work with normal maps, since the entry set compares
+   * with == instead of .equals.
+   *
+   * @param o the object to compare to
+   * @return true if it is equal
+   */
+  public boolean equals(Object o)
+  {
+    // Why did Sun specify this one? The superclass does the right thing.
+    return super.equals(o);
+  }
 
-      public boolean remove (Object o)
-      {
-       if (! (o instanceof Map.Entry))
-         return false;
-       Map.Entry m = (Map.Entry) o;
-       if (IdentityHashMap.this.containsKey (m.getKey ())
-           && IdentityHashMap.this.get (m.getKey ()) == m.getValue ())
-         {
-           int oldsize = size;
-           IdentityHashMap.this.remove (m.getKey ());
-           return oldsize != size;
-         }
-       return false;
-      }
-    };
+  /**
+   * Return the value in this Map associated with the supplied key,
+   * or <pre>null</pre> if the key maps to nothing.  NOTE: Since the value
+   * could also be null, you must use containsKey to see if this key
+   * actually maps to something.  Unlike normal maps, this tests for the key
+   * with <code>entry == key</code> instead of
+   * <code>entry == null ? key == null : entry.equals(key)</code>.
+   *
+   * @param key the key for which to fetch an associated value
+   * @return what the key maps to, if present
+   * @see #put(Object, Object)
+   * @see #containsKey(Object)
+   */
+  public Object get(Object key)
+  {
+    int h = hash(key);
+    return table[h] == key ? table[h + 1] : null;
   }
 
-  public Object get (Object key)
+  /**
+   * Returns the hashcode of this map. This guarantees that two
+   * IdentityHashMaps that compare with equals() will have the same hash code,
+   * but may break with comparison to normal maps since it uses
+   * System.identityHashCode() instead of hashCode().
+   *
+   * @return the hash code
+   */
+  public int hashCode()
   {
-    int h = getHash (key);
-    int save = h;
-    while (true)
+    int hash = 0;
+    for (int i = table.length - 2; i >= 0; i -= 2)
       {
-       if (table[h] == key)
-         return table[h + 1];
-       if (table[h] == emptyslot)
-         return null;
-       h += 2;
-       if (h >= table.length)
-         h = 0;
-       if (h == save)
-         return null;
+        Object key = table[i];
+        if (key == emptyslot || key == tombstone)
+          continue;
+        hash += (System.identityHashCode(key)
+                 ^ System.identityHashCode(table[i + 1]));
       }
+    return hash;
   }
 
-  public boolean isEmpty ()
+  /**
+   * Returns true if there are no key-value mappings currently in this Map
+   * @return <code>size() == 0</code>
+   */
+  public boolean isEmpty()
   {
     return size == 0;
   }
 
-  public Set keySet ()
+  /**
+   * Returns a "set view" of this Map's keys. The set is backed by the
+   * Map, so changes in one show up in the other.  The set supports
+   * element removal, but not element addition.
+   * <p>
+   *
+   * <em>The semantics of this set are different from the contract of Set
+   * in order to make IdentityHashMap work.  This means that while you can
+   * compare these objects between IdentityHashMaps, comparing them with
+   * regular sets is likely to have undefined behavior.</em>  The hashCode
+   * of the set is the sum of the identity hash codes, instead of the
+   * regular hashCodes, and equality is determined by reference instead
+   * of by the equals method.
+   * <p>
+   *
+   * @return a set view of the keys
+   * @see #values()
+   * @see #entrySet()
+   */
+  public Set keySet()
   {
-    return new AbstractSet ()
-    {
-      public int size ()
-      {
-       return size;
-      }
-
-      public Iterator iterator ()
-      {
-       return new IdentityIterator (IdentityIterator.KEYS);
-      }
-
-      public void clear ()
+    if (keys == null)
+      keys = new AbstractSet()
       {
-       IdentityHashMap.this.clear ();
-      }
-
-      public boolean contains (Object o)
-      {
-       return IdentityHashMap.this.containsKey (o);
-      }
-
-      public boolean remove (Object o)
-      {
-       int oldsize = size;
-       IdentityHashMap.this.remove (o);
-       return oldsize != size;
-      }
-    };
+        public int size()
+        {
+          return size;
+        }
+
+        public Iterator iterator()
+        {
+          return new IdentityIterator(KEYS);
+        }
+
+        public void clear()
+        {
+          IdentityHashMap.this.clear();
+        }
+
+        public boolean contains(Object o)
+        {
+          return containsKey(o);
+        }
+
+        public int hashCode()
+        {
+          int hash = 0;
+          for (int i = table.length - 2; i >= 0; i -= 2)
+            {
+              Object key = table[i];
+              if (key == emptyslot || key == tombstone)
+                continue;
+              hash += System.identityHashCode(key);
+            }
+          return hash;
+
+        }
+
+        public boolean remove(Object o)
+        {
+          int h = hash(o);
+          if (table[h] == o)
+            {
+              size--;
+              modCount++;
+              table[h] = tombstone;
+              table[h + 1] = tombstone;
+              return true;
+            }
+          return false;
+        }
+      };
+    return keys;
   }
 
-  public Object put (Object key, Object value)
+  /**
+   * Puts the supplied value into the Map, mapped by the supplied key.
+   * The value may be retrieved by any object which <code>equals()</code>
+   * this key. NOTE: Since the prior value could also be null, you must
+   * first use containsKey if you want to see if you are replacing the
+   * key's mapping.  Unlike normal maps, this tests for the key
+   * with <code>entry == key</code> instead of
+   * <code>entry == null ? key == null : entry.equals(key)</code>.
+   *
+   * @param key the key used to locate the value
+   * @param value the value to be stored in the HashMap
+   * @return the prior mapping of the key, or null if there was none
+   * @see #get(Object)
+   */
+  public Object put(Object key, Object value)
   {
-    // Rehash if the load factor is too high.  We use a factor of 1.5
-    // -- the division by 2 is implicit on both sides.
-    if (size * 3 > table.length)
-      {
-       Object[] old = table;
-       table = new Object[old.length * 2];
-       Arrays.fill (table, emptyslot);
-       size = 0;
-       for (int i = 0; i < old.length; i += 2)
-         {
-           if (old[i] != tombstone && old[i] != emptyslot)
-             {
-               // Just use put.  This isn't very efficient, but it is
-               // ok.
-               put (old[i], old[i + 1]);
-             }
-         }
-      }
-
-    int h = getHash (key);
-    int save = h;
-    int del = -1;
-    while (true)
+    // Rehash if the load factor is too high.
+    if (size > threshold)
       {
-       if (table[h] == key)
-         {
-           Object r = table[h + 1];
-           table[h + 1] = value;
-           return r;
-         }
-       else if (table[h] == tombstone && del == -1)
-         del = h;
-       else if (table[h] == emptyslot)
-         {
-           if (del == -1)
-             del = h;
-           break;
-         }
-       h += 2;
-       if (h >= table.length)
-         h = 0;
-       if (h == save)
-         break;
+        Object[] old = table;
+        // This isn't necessarily prime, but it is an odd number of key/value
+        // slots, which has a higher probability of fewer collisions.
+        table = new Object[old.length * 2 + 2];
+        Arrays.fill(table, emptyslot);
+        size = 0;
+        threshold = table.length / 4 * 3;
+
+        for (int i = old.length - 2; i >= 0; i -= 2)
+          {
+            Object oldkey = old[i];
+            if (oldkey != tombstone && oldkey != emptyslot)
+              // Just use put.  This isn't very efficient, but it is ok.
+              put(oldkey, old[i + 1]);
+          }
       }
 
-    if (del != -1)
+    int h = hash(key);
+    if (table[h] == key)
       {
-       table[del] = key;
-       table[del + 1] = value;
-       ++size;
-       return null;
+        Object r = table[h + 1];
+        table[h + 1] = value;
+        return r;
       }
 
-    // This is an error.
+    // At this point, we add a new mapping.
+    modCount++;
+    size++;
+    table[h] = key;
+    table[h + 1] = value;
     return null;
   }
 
-  public Object remove (Object key)
+  /**
+   * Copies all of the mappings from the specified map to this. If a key
+   * is already in this map, its value is replaced.
+   *
+   * @param m the map to copy
+   * @throws NullPointerException if m is null
+   */
+  public void putAll(Map m)
   {
-    int h = getHash (key);
-    int save = h;
-    while (true)
+    // Why did Sun specify this one? The superclass does the right thing.
+    super.putAll(m);
+  }
+
+  /**
+   * Removes from the HashMap and returns the value which is mapped by the
+   * supplied key. If the key maps to nothing, then the HashMap remains
+   * unchanged, and <pre>null</pre> is returned. NOTE: Since the value
+   * could also be null, you must use containsKey to see if you are
+   * actually removing a mapping.  Unlike normal maps, this tests for the
+   * key with <code>entry == key</code> instead of
+   * <code>entry == null ? key == null : entry.equals(key)</code>.
+   *
+   * @param key the key used to locate the value to remove
+   * @return whatever the key mapped to, if present
+   */
+  public Object remove(Object key)
+  {
+    int h = hash(key);
+    if (table[h] == key)
       {
-       if (table[h] == key)
-         {
-           Object r = table[h + 1];
-           table[h] = tombstone;
-           table[h + 1] = tombstone;
-           --size;
-           return r;
-         }
-       h += 2;
-       if (h >= table.length)
-         h = 0;
-       if (h == save)
-         break;
+        modCount++;
+        size--;
+        Object r = table[h + 1];
+        table[h] = tombstone;
+        table[h + 1] = tombstone;
+        return r;
       }
-
     return null;
   }
 
-  public int size ()
+  /**
+   * Returns the number of kay-value mappings currently in this Map
+   * @return the size
+   */
+  public int size()
   {
     return size;
   }
 
-  public Collection values ()
+  /**
+   * Returns a "collection view" (or "bag view") of this Map's values.
+   * The collection is backed by the Map, so changes in one show up
+   * in the other.  The collection supports element removal, but not element
+   * addition.
+   * <p>
+   *
+   * <em>The semantics of this set are different from the contract of
+   * Collection in order to make IdentityHashMap work.  This means that
+   * while you can compare these objects between IdentityHashMaps, comparing
+   * them with regular sets is likely to have undefined behavior.</em>
+   * Likewise, contains and remove go by == instead of equals().
+   * <p>
+   *
+   * @return a bag view of the values
+   * @see #keySet()
+   * @see #entrySet()
+   */
+  public Collection values()
   {
-    return new AbstractCollection ()
-    {
-      public int size ()
+    if (values == null)
+      values = new AbstractCollection()
       {
-       return size;
-      }
+        public int size()
+        {
+          return size;
+        }
+
+        public Iterator iterator()
+        {
+          return new IdentityIterator(VALUES);
+        }
+
+        public void clear()
+        {
+          IdentityHashMap.this.clear();
+        }
+
+        public boolean remove(Object o)
+        {
+          for (int i = table.length - 1; i > 0; i -= 2)
+            if (table[i] == o)
+              {
+                modCount++;
+                table[i - 1] = tombstone;
+                table[i] = tombstone;
+                size--;
+                return true;
+              }
+          return false;
+        }
+      };
+    return values;
+  }
 
-      public Iterator iterator ()
-      {
-       return new IdentityIterator (IdentityIterator.VALUES);
-      }
+  /**
+   * Helper method which computes the hash code, then traverses the table
+   * until it finds the key, or the spot where the key would go.
+   *
+   * @param key the key to check
+   * @return the index where the key belongs
+   * @see #IdentityHashMap(int)
+   * @see #put(Object, Object)
+   */
+  // Package visible for use by nested classes.
+  int hash(Object key)
+  {
+    // Implementation note: it is feasible for the table to have no
+    // emptyslots, if it is full with entries and tombstones, so we must
+    // remember where we started. If we encounter the key or an emptyslot,
+    // we are done.  If we encounter a tombstone, the key may still be in
+    // the array.  If we don't encounter the key, we use the first emptyslot
+    // or tombstone we encountered as the location where the key would go.
+    // By requiring at least 2 key/value slots, and rehashing at 75%
+    // capacity, we guarantee that there will always be either an emptyslot
+    // or a tombstone somewhere in the table.
+    int h = 2 * Math.abs(System.identityHashCode(key) % table.length);
+    int del = -1;
+    int save = h;
 
-      public void clear ()
+    do
       {
-       IdentityHashMap.this.clear ();
+        if (table[h] == key)
+          return h;
+        if (table[h] == emptyslot)
+          break;
+        if (table[h] == tombstone && del < 0)
+          del = h;
+        h -= 2;
+        if (h < 0)
+          h = table.length - 2;
       }
-    };
+    while (h != save);
+
+    return del < 0 ? h : del;
   }
 
-  private class IdentityIterator implements Iterator
+  /**
+   * This class allows parameterized iteration over IdentityHashMaps.  Based
+   * on its construction, it returns the key or value of a mapping, or
+   * creates the appropriate Map.Entry object with the correct fail-fast
+   * semantics and identity comparisons.
+   *
+   * @author Tom Tromey <tromey@redhat.com>
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private final class IdentityIterator implements Iterator
   {
-    static final int KEYS = 0;
-    static final int VALUES = 1;
-    static final int ENTRIES = 2;
-
-    // Type of iterator.
-    int type;
-    // Location in the table.
-    int loc;
-    // How many items we've seen.
-    int seen;
-
-    IdentityIterator (int type)
+    /**
+     * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
+     * or {@link #ENTRIES}.
+     */
+    final int type;
+    /** The number of modifications to the backing Map that we know about. */
+    int knownMod = modCount;
+    /** The number of elements remaining to be returned by next(). */
+    int count = size;
+    /** Location in the table. */
+    int loc = table.length;
+
+    /**
+     * Construct a new Iterator with the supplied type.
+     * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+     */
+    IdentityIterator(int type)
     {
       this.type = type;
-      loc = 0;
-      seen = 0;
     }
 
-    public boolean hasNext ()
+    /**
+     * Returns true if the Iterator has more elements.
+     * @return true if there are more elements
+     * @throws ConcurrentModificationException if the Map was modified
+     */
+    public boolean hasNext()
     {
-      return seen < size;
+      if (knownMod != modCount)
+        throw new ConcurrentModificationException();
+      return count > 0;
     }
 
-    public Object next ()
+    /**
+     * Returns the next element in the Iterator's sequential view.
+     * @return the next element
+     * @throws ConcurrentModificationException if the Map was modified
+     * @throws NoSuchElementException if there is none
+     */
+    public Object next()
     {
-      while (true)
-       {
-         loc += 2;
-         if (loc >= table.length)
-           throw new NoSuchElementException ();
-         if (table[loc] != tombstone && table[loc] != emptyslot)
-           {
-             ++seen;
-             return table[loc];
-           }
-       }
+      if (knownMod != modCount)
+        throw new ConcurrentModificationException();
+      if (count == 0)
+        throw new NoSuchElementException();
+      count--;
+
+      Object key;
+      do
+        {
+          loc -= 2;
+          key = table[loc];
+        }
+      while (key == emptyslot || key == tombstone);
+
+      return type == KEYS ? key : (type == VALUES ? table[loc + 1]
+                                   : new IdentityEntry(loc));
     }
 
-    public void remove ()
+    /**
+     * Removes from the backing Map the last element which was fetched
+     * with the <pre>next()</pre> method.
+     * @throws ConcurrentModificationException if the Map was modified
+     * @throws IllegalStateException if called when there is no last element
+     */
+    public void remove()
     {
-      if (loc >= table.length
-         || table[loc] == tombstone
-         || table[loc] == emptyslot)
-       throw new IllegalStateException ();
+      if (knownMod != modCount)
+        throw new ConcurrentModificationException();
+      if (loc == table.length || table[loc] == tombstone)
+        throw new IllegalStateException();
+      modCount++;
+      size--;
       table[loc] = tombstone;
       table[loc + 1] = tombstone;
-      --size;
+      knownMod++;
     }
-  }
+  } // class IdentityIterator
+
+  /**
+   * This class provides Map.Entry objects for IdentityHashMaps.  The entry
+   * is fail-fast, and will throw a ConcurrentModificationException if
+   * the underlying map is modified, or if remove is called on the iterator
+   * that generated this object.  It is identity based, so it violates
+   * the general contract of Map.Entry, and is probably unsuitable for
+   * comparison to normal maps; but it works among other IdentityHashMaps.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private final class IdentityEntry implements Map.Entry
+  {
+    /** The location of this entry. */
+    final int loc;
+    /** The number of modifications to the backing Map that we know about. */
+    final int knownMod = modCount;
+
+    /**
+     * Constructs the Entry.
+     *
+     * @param loc the location of this entry in table
+     */
+    IdentityEntry(int loc)
+    {
+      this.loc = loc;
+    }
+
+    /**
+     * Compares the specified object with this entry, using identity
+     * semantics. Note that this can lead to undefined results with
+     * Entry objects created by normal maps.
+     *
+     * @param o the object to compare
+     * @return true if it is equal
+     * @throws ConcurrentModificationException if the entry was invalidated
+     *         by modifying the Map or calling Iterator.remove()
+     */
+    public boolean equals(Object o)
+    {
+      if (knownMod != modCount || table[loc] == tombstone)
+        throw new ConcurrentModificationException();
+      if (! (o instanceof Map.Entry))
+        return false;
+      Map.Entry e = (Map.Entry) o;
+      return table[loc] == e.getKey() && table[loc + 1] == e.getValue();
+    }
+
+    /**
+     * Returns the key of this entry.
+     *
+     * @return the key
+     * @throws ConcurrentModificationException if the entry was invalidated
+     *         by modifying the Map or calling Iterator.remove()
+     */
+    public Object getKey()
+    {
+      if (knownMod != modCount || table[loc] == tombstone)
+        throw new ConcurrentModificationException();
+      return table[loc];
+    }
+
+    /**
+     * Returns the value of this entry.
+     *
+     * @return the value
+     * @throws ConcurrentModificationException if the entry was invalidated
+     *         by modifying the Map or calling Iterator.remove()
+     */
+    public Object getValue()
+    {
+      if (knownMod != modCount || table[loc] == tombstone)
+        throw new ConcurrentModificationException();
+      return table[loc + 1];
+    }
+
+    /**
+     * Returns the hashcode of the entry, using identity semantics.
+     * Note that this can lead to undefined results with Entry objects
+     * created by normal maps.
+     *
+     * @return the hash code
+     * @throws ConcurrentModificationException if the entry was invalidated
+     *         by modifying the Map or calling Iterator.remove()
+     */
+    public int hashCode()
+    {
+      if (knownMod != modCount || table[loc] == tombstone)
+        throw new ConcurrentModificationException();
+      return (System.identityHashCode(table[loc])
+              ^ System.identityHashCode(table[loc + 1]));
+    }
+
+    /**
+     * Replaces the value of this mapping, and returns the old value.
+     *
+     * @param value the new value
+     * @return the old value
+     * @throws ConcurrentModificationException if the entry was invalidated
+     *         by modifying the Map or calling Iterator.remove()
+     */
+    public Object setValue(Object value)
+    {
+      if (knownMod != modCount || table[loc] == tombstone)
+        throw new ConcurrentModificationException();
+      Object r = table[loc + 1];
+      table[loc + 1] = value;
+      return r;
+    }
+
+    /**
+     * This provides a string representation of the entry. It is of the form
+     * "key=value", where string concatenation is used on key and value.
+     *
+     * @return the string representation
+     * @throws ConcurrentModificationException if the entry was invalidated
+     *         by modifying the Map or calling Iterator.remove()
+     */
+    public final String toString()
+    {
+      if (knownMod != modCount || table[loc] == tombstone)
+        throw new ConcurrentModificationException();
+      return table[loc] + "=" + table[loc + 1];
+    }
+  } // class IdentityEntry
 
-  private void readObject (ObjectInputStream s)
+  /**
+   * Reads the object from a serial stream.
+   *
+   * @param s the stream to read from
+   * @throws ClassNotFoundException if the underlying stream fails
+   * @throws IOException if the underlying stream fails
+   * @serialData expects the size (int), followed by that many key (Object)
+   *             and value (Object) pairs, with the pairs in no particular
+   *             order
+   */
+  private void readObject(ObjectInputStream s)
     throws IOException, ClassNotFoundException
   {
-    int num = s.readInt ();
-    for (int i = 0; i < num; ++i)
-      {
-       Object key = s.readObject ();
-       Object value = s.readObject ();
-       put (key, value);
-      }
+    s.defaultReadObject();
+
+    int num = s.readInt();
+    table = new Object[2 * Math.max(num * 2, DEFAULT_CAPACITY)];
+    // Read key/value pairs.
+    while (--num >= 0)
+      put(s.readObject(), s.readObject());
   }
 
-  private void writeObject (ObjectOutputStream s)
+  /**
+   * Writes the object to a serial stream.
+   *
+   * @param s the stream to write to
+   * @throws IOException if the underlying stream fails
+   * @serialData outputs the size (int), followed by that many key (Object)
+   *             and value (Object) pairs, with the pairs in no particular
+   *             order
+   */
+  private void writeObject(ObjectOutputStream s)
     throws IOException
   {
-    s.writeInt (size);
-    Iterator it = entrySet ().iterator ();
-    while (it.hasNext ())
+    s.defaultWriteObject();
+    s.writeInt(size);
+    for (int i = table.length - 2; i >= 0; i -= 2)
       {
-       Map.Entry entry = (Map.Entry) it.next ();
-       s.writeObject (entry.getKey ());
-       s.writeObject (entry.getValue ());
+        Object key = table[i];
+        if (key != tombstone && key != emptyslot)
+          {
+            s.writeObject(key);
+            s.writeObject(table[i + 1]);
+          }
       }
   }
-
-  // Compute the hash value we will use for an object.
-  private int getHash (Object o)
-  {
-    return 2 * Math.abs (System.identityHashCode (o) % (table.length / 2));
-  }
-
-  // Number of items in hash table.
-  private int size;
-  // The table itself.
-  private Object[] table;
-
-  // This object is used to mark deleted items.
-  private static final Object tombstone = new Object ();
-  // This object is used to mark empty slots.  We need this because
-  // using null is ambiguous.
-  private static final Object emptyslot = new Object ();
 }
index 8950d58970b538b94d152a3c74d4df62921b3780..8503e375ace4d60af4d82f82855bd508874e0219 100644 (file)
@@ -28,11 +28,6 @@ executable file might be covered by the GNU General Public License. */
 
 package java.util;
 
-import java.io.IOException;
-import java.io.Serializable;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
 /**
  * This class provides a hashtable-backed implementation of the
  * Map interface, with predictable traversal order.
@@ -89,6 +84,7 @@ import java.io.ObjectOutputStream;
  * @see TreeMap
  * @see Hashtable
  * @since 1.4
+ * @status updated to 1.4
  */
 public class LinkedHashMap extends HashMap
 {
@@ -218,8 +214,8 @@ public class LinkedHashMap extends HashMap
    * Construct a new insertion-ordered LinkedHashMap with a specific
    * inital capacity and default load factor of 0.75.
    *
-   * @param initialCapacity the initial capacity of this HashMap (>=0)
-   * @throws IllegalArgumentException if (initialCapacity < 0)
+   * @param initialCapacity the initial capacity of this HashMap (&gt;= 0)
+   * @throws IllegalArgumentException if (initialCapacity &lt; 0)
    */
   public LinkedHashMap(int initialCapacity)
   {
@@ -231,10 +227,10 @@ public class LinkedHashMap extends HashMap
    * Construct a new insertion-orderd LinkedHashMap with a specific
    * inital capacity and load factor.
    *
-   * @param initialCapacity the initial capacity (>=0)
-   * @param loadFactor the load factor (>0, not NaN)
-   * @throws IllegalArgumentException if (initialCapacity < 0) ||
-   *                                     ! (loadFactor > 0.0)
+   * @param initialCapacity the initial capacity (&gt;= 0)
+   * @param loadFactor the load factor (&gt; 0, not NaN)
+   * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
+   *                                     ! (loadFactor &gt; 0.0)
    */
   public LinkedHashMap(int initialCapacity, float loadFactor)
   {
@@ -281,7 +277,7 @@ public class LinkedHashMap extends HashMap
     LinkedHashEntry e = head;
     while (e != null)
       {
-        if (value == null ? e.value == null : value.equals(e.value))
+        if (equals(value, e.value))
           return true;
         e = e.succ;
       }
@@ -307,7 +303,7 @@ public class LinkedHashMap extends HashMap
     HashEntry e = buckets[idx];
     while (e != null)
       {
-        if (key == null ? e.key == null : key.equals(e.key))
+        if (equals(key, e.key))
           {
             if (accessOrder)
               {
@@ -376,13 +372,14 @@ public class LinkedHashMap extends HashMap
     return false;
   }
 
-  /** Helper method called by <code>put</code>, which creates and adds a
+  /**
+   * Helper method called by <code>put</code>, which creates and adds a
    * new Entry, followed by performing bookkeeping (like removeEldestEntry).
    *
    * @param key the key of the new Entry
    * @param value the value
    * @param idx the index in buckets where the new Entry belongs
-   * @param callRemove Whether to call the removeEldestEntry method.
+   * @param callRemove whether to call the removeEldestEntry method
    * @see #put(Object, Object)
    * @see #removeEldestEntry(Map.Entry)
    */
@@ -397,6 +394,11 @@ public class LinkedHashMap extends HashMap
       remove(head);
   }
 
+  /**
+   * Helper method, called by clone() to reset the doubly-linked list.
+   * @param m the map to add entries from
+   * @see #clone()
+   */
   void putAllInternal(Map m)
   {
     head = null;
@@ -466,8 +468,8 @@ public class LinkedHashMap extends HashMap
           throw new IllegalStateException();
 
         LinkedHashMap.this.remove(last.key);
-        knownMod++;
         last = null;
+        knownMod++;
       }
     };
   }
diff --git a/libjava/java/util/LinkedHashSet.java b/libjava/java/util/LinkedHashSet.java
new file mode 100644 (file)
index 0000000..f98c32e
--- /dev/null
@@ -0,0 +1,149 @@
+/* LinkedHashSet.java -- a set backed by a LinkedHashMap, for linked
+   list traversal.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
+
+
+package java.util;
+
+import java.io.Serializable;
+
+/**
+ * This class provides a hashtable-backed implementation of the
+ * Set interface, with predictable traversal order.
+ * <p>
+ *
+ * It uses a hash-bucket approach; that is, hash collisions are handled
+ * by linking the new node off of the pre-existing node (or list of
+ * nodes).  In this manner, techniques such as linear probing (which
+ * can cause primary clustering) and rehashing (which does not fit very
+ * well with Java's method of precomputing hash codes) are avoided.  In
+ * addition, this maintains a doubly-linked list which tracks insertion
+ * order.  Note that the insertion order is not modified if an
+ * <code>add</code> simply reinserts an element in the set.
+ * <p>
+ *
+ * One of the nice features of tracking insertion order is that you can
+ * copy a set, and regardless of the implementation of the original,
+ * produce the same results when iterating over the copy.  This is possible
+ * without needing the overhead of <code>TreeSet</code>.
+ * <p>
+ *
+ * Under ideal circumstances (no collisions), LinkedHashSet offers O(1) 
+ * performance on most operations.  In the worst case (all elements map
+ * to the same hash code -- very unlikely), most operations are O(n).
+ * <p>
+ *
+ * LinkedHashSet accepts the null entry.  It is not synchronized, so if
+ * you need multi-threaded access, consider using:<br>
+ * <code>Set s = Collections.synchronizedSet(new LinkedHashSet(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Object#hashCode()
+ * @see Collection
+ * @see Set
+ * @see HashSet
+ * @see TreeSet
+ * @see Collections#synchronizedSet(Set)
+ * @since 1.4
+ * @status updated to 1.4
+ */
+public class LinkedHashSet extends HashSet
+  implements Set, Cloneable, Serializable
+{
+  /**
+   * Compatible with JDK 1.4.
+   */
+  private static final long serialVersionUID = -2851667679971038690L;
+
+  /**
+   * Construct a new, empty HashSet whose backing HashMap has the default
+   * capacity (11) and loadFacor (0.75).
+   */
+  public LinkedHashSet()
+  {
+    super();
+  }
+
+  /**
+   * Construct a new, empty HashSet whose backing HashMap has the supplied
+   * capacity and the default load factor (0.75).
+   *
+   * @param initialCapacity the initial capacity of the backing HashMap
+   * @throws IllegalArgumentException if the capacity is negative
+   */
+  public LinkedHashSet(int initialCapacity)
+  {
+    super(initialCapacity);
+  }
+
+  /**
+   * Construct a new, empty HashSet whose backing HashMap has the supplied
+   * capacity and load factor.
+   *
+   * @param initialCapacity the initial capacity of the backing HashMap
+   * @param loadFactor the load factor of the backing HashMap
+   * @throws IllegalArgumentException if either argument is negative, or
+   *         if loadFactor is POSITIVE_INFINITY or NaN
+   */
+  public LinkedHashSet(int initialCapacity, float loadFactor)
+  {
+    super(initialCapacity, loadFactor);
+  }
+
+  /**
+   * Construct a new HashSet with the same elements as are in the supplied
+   * collection (eliminating any duplicates, of course). The backing storage
+   * has twice the size of the collection, or the default size of 11,
+   * whichever is greater; and the default load factor (0.75).
+   *
+   * @param c a collection of initial set elements
+   * @throws NullPointerException if c is null
+   */
+  public LinkedHashSet(Collection c)
+  {
+    super(c);
+  }
+
+  /**
+   * Helper method which initializes the backing Map.
+   *
+   * @param capacity the initial capacity
+   * @param load the initial load factor
+   * @return the backing HashMap
+   */
+  HashMap init(int capacity, float load)
+  {
+    return new LinkedHashMap(capacity, load);
+  }
+
+}
index 41ca0936cf2123f2edbbe16cae28a4e42834acb4..f2f333c4ccffc5e935197ab3c7f6db1fa78a40ca 100644 (file)
@@ -1,5 +1,5 @@
 /* LinkedList.java -- Linked list implementation of the List interface
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -32,25 +32,50 @@ import java.io.ObjectInputStream;
 import java.io.IOException;
 import java.lang.reflect.Array;
 
-// TO DO:
-// ~ Doc comment for the class.
-// ~ Doc comments for the non-list methods.
-// ~ other general implementation notes.
-
 /**
- * Linked list implementation of the List interface.
+ * Linked list implementation of the List interface. In addition to the
+ * methods of the List interface, this class provides access to the first
+ * and last list elements in O(1) time for easy stack, queue, or double-ended
+ * queue (deque) creation. The list is doubly-linked, with traversal to a
+ * given index starting from the end closest to the element.<p>
+ *
+ * LinkedList is not synchronized, so if you need multi-threaded access,
+ * consider using:<br>
+ * <code>List l = Collections.synchronizedList(new LinkedList(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see List
+ * @see ArrayList
+ * @see Vector
+ * @see Collections#synchronizedList(List)
+ * @since 1.2
+ * @status missing javadoc, but complete to 1.4
  */
 public class LinkedList extends AbstractSequentialList
   implements List, Cloneable, Serializable
 {
-  static final long serialVersionUID = 876323262645176354L;
+  /**
+   * Compatible with JDK 1.2.
+   */
+  private static final long serialVersionUID = 876323262645176354L;
 
   /**
-   * An Entry containing the head (in the next field) and the tail (in the
-   * previous field) of the list. The data field is null. If the list is empty,
-   * both the head and the tail point to ends itself.
+   * The first element in the list.
    */
   transient Entry first;
+
+  /**
+   * The last element in the list.
+   */
   transient Entry last;
 
   /**
@@ -61,18 +86,27 @@ public class LinkedList extends AbstractSequentialList
   /**
    * Class to represent an entry in the list. Holds a single element.
    */
-  private static class Entry
+  private static final class Entry
   {
+    /** The element in the list. */
     Object data;
+
+    /** The next list entry, null if this is last. */
     Entry next;
+
+    /** The previous list entry, null if this is first. */
     Entry previous;
-    
+
+    /**
+     * Construct an entry.
+     * @param data the list element
+     */
     Entry(Object data)
     {
       this.data = data;
     }
-  }
-  
+  } // class Entry
+
   /**
    * Obtain the Entry at a given position in a list. This method of course
    * takes linear time, but it is intelligent enough to take the shorter of the
@@ -82,7 +116,8 @@ public class LinkedList extends AbstractSequentialList
    * For speed and flexibility, range checking is not done in this method:
    * Incorrect values will be returned if (n < 0) or (n >= size).
    *
-   * @param n the number of the entry to get.
+   * @param n the number of the entry to get
+   * @return the entry at position n
    */
   private Entry getEntry(int n)
   {
@@ -90,50 +125,76 @@ public class LinkedList extends AbstractSequentialList
     if (n < size / 2)
       {
         e = first;
-       // n less than size/2, iterate from start
-       while (n-- > 0)
-         {
-           e = e.next;
-         }
+        // n less than size/2, iterate from start
+        while (n-- > 0)
+          e = e.next;
       }
     else
       {
-        e = last;      
-       // n greater than size/2, iterate from end
-       while (++n < size)
-         {
-           e = e.previous;
-         }
+        e = last;
+        // n greater than size/2, iterate from end
+        while (++n < size)
+          e = e.previous;
       }
     return e;
   }
-  
-  /** Remove an entry from the list. This will adjust size and deal with
-   *  `first' and  `last' appropriatly. It does not effect modCount, that is 
-   *  the responsibility of the caller. */
+
+  /**
+   * Remove an entry from the list. This will adjust size and deal with
+   *  `first' and  `last' appropriatly.
+   *
+   * @param e the entry to remove
+   */
   private void removeEntry(Entry e)
   {
-    if (size == 1)
+    modCount++;
+    size--;
+    if (size == 0)
       first = last = null;
     else
       {
-       if (e == first)
-         {
-           first = e.next;
-           e.next.previous = null;
-         }
-       else if (e == last)
-         {
-           last = e.previous;
-           e.previous.next = null;
-         }
-       else
-         {
-           e.next.previous = e.previous;       
-           e.previous.next = e.next;
-         }
+        if (e == first)
+          {
+            first = e.next;
+            e.next.previous = null;
+          }
+        else if (e == last)
+          {
+            last = e.previous;
+            e.previous.next = null;
+          }
+        else
+          {
+            e.next.previous = e.previous;
+            e.previous.next = e.next;
+          }
       }
-    size--;
+  }
+
+  /**
+   * Checks that the index is in the range of possible elements (inclusive).
+   *
+   * @param index the index to check
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size
+   */
+  private void checkBoundsInclusive(int index)
+  {
+    if (index < 0 || index > size)
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+                                          + size);
+  }
+
+  /**
+   * Checks that the index is in the range of existing elements (exclusive).
+   *
+   * @param index the index to check
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size
+   */
+  private void checkBoundsExclusive(int index)
+  {
+    if (index < 0 || index >= size)
+      throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+                                          + size);
   }
 
   /**
@@ -141,24 +202,26 @@ public class LinkedList extends AbstractSequentialList
    */
   public LinkedList()
   {
-    super();
   }
 
   /**
    * Create a linked list containing the elements, in order, of a given
    * collection.
    *
-   * @param c the collection to populate this list from.
+   * @param c the collection to populate this list from
+   * @throws NullPointerException if c is null
    */
   public LinkedList(Collection c)
   {
-    super();
-    // Note: addAll could be made slightly faster, but not enough so to justify
-    // re-implementing it from scratch. It is just a matter of a relatively
-    // small constant factor.
     addAll(c);
   }
 
+  /**
+   * Returns the first element in the list.
+   *
+   * @return the first list element
+   * @throws NoSuchElementException if the list is empty
+   */
   public Object getFirst()
   {
     if (size == 0)
@@ -166,6 +229,12 @@ public class LinkedList extends AbstractSequentialList
     return first.data;
   }
 
+  /**
+   * Returns the last element in the list.
+   *
+   * @return the last list element
+   * @throws NoSuchElementException if the list is empty
+   */
   public Object getLast()
   {
     if (size == 0)
@@ -173,273 +242,374 @@ public class LinkedList extends AbstractSequentialList
     return last.data;
   }
 
+  /**
+   * Remove and return the first element in the list.
+   *
+   * @return the former first element in the list
+   * @throws NoSuchElementException if the list is empty
+   */
   public Object removeFirst()
   {
     if (size == 0)
       throw new NoSuchElementException();
-    size--;
     modCount++;
+    size--;
     Object r = first.data;
-    
+
     if (first.next != null)
       first.next.previous = null;
     else
       last = null;
 
     first = first.next;
-    
+
     return r;
   }
 
+  /**
+   * Remove and return the last element in the list.
+   *
+   * @return the former last element in the list
+   * @throws NoSuchElementException if the list is empty
+   */
   public Object removeLast()
   {
     if (size == 0)
       throw new NoSuchElementException();
-    size--;
     modCount++;
+    size--;
     Object r = last.data;
-    
+
     if (last.previous != null)
       last.previous.next = null;
     else
       first = null;
-    
+
     last = last.previous;
-    
+
     return r;
   }
 
+  /**
+   * Insert an element at the first of the list.
+   *
+   * @param o the element to insert
+   */
   public void addFirst(Object o)
   {
-    modCount++;
     Entry e = new Entry(o);
-    
+
+    modCount++;
     if (size == 0)
       first = last = e;
     else
       {
-       e.next = first;
+        e.next = first;
         first.previous = e;
-       first = e;
-      }    
+        first = e;
+      }
     size++;
   }
 
+  /**
+   * Insert an element at the last of the list.
+   *
+   * @param o the element to insert
+   */
   public void addLast(Object o)
   {
-    modCount++;
     addLastEntry(new Entry(o));
   }
-  
+
+  /**
+   * Inserts an element at the end of the list.
+   *
+   * @param e the entry to add
+   */
   private void addLastEntry(Entry e)
   {
+    modCount++;
     if (size == 0)
       first = last = e;
     else
       {
-       e.previous = last;
+        e.previous = last;
         last.next = e;
-       last = e;
+        last = e;
       }
     size++;
   }
 
+  /**
+   * Returns true if the list contains the given object. Comparison is done by
+   * <code>o == null ? e = null : o.equals(e)</code>.
+   *
+   * @param o the element to look for
+   * @return true if it is found
+   */
   public boolean contains(Object o)
   {
     Entry e = first;
     while (e != null)
       {
-        if (e.data == null ? o == null : o.equals(e.data))
-         return true;
+        if (equals(o, e.data))
+          return true;
         e = e.next;
       }
     return false;
   }
 
+  /**
+   * Returns the size of the list.
+   *
+   * @return the list size
+   */
   public int size()
   {
     return size;
   }
-  
+
+  /**
+   * Adds an element to the end of the list.
+   *
+   * @param e the entry to add
+   * @return true, as it always succeeds
+   */
   public boolean add(Object o)
   {
-    modCount++;
     addLastEntry(new Entry(o));
     return true;
   }
-  
+
+  /**
+   * Removes the entry at the lowest index in the list that matches the given
+   * object, comparing by <code>o == null ? e = null : o.equals(e)</code>.
+   *
+   * @param o the object to remove
+   * @return true if an instance of the object was removed
+   */
   public boolean remove(Object o)
   {
-    modCount++;
     Entry e = first;
     while (e != null)
       {
-        if (e.data == null ? o == null : o.equals(e.data))
-         {
-           removeEntry(e);
-           return true;
-         }
+        if (equals(o, e.data))
+          {
+            removeEntry(e);
+            return true;
+          }
         e = e.next;
       }
     return false;
   }
 
+  /**
+   * Append the elements of the collection in iteration order to the end of
+   * this list. If this list is modified externally (for example, if this
+   * list is the collection), behavior is unspecified.
+   *
+   * @param c the collection to append
+   * @return true if the list was modified
+   * @throws NullPointerException if c is null
+   */
   public boolean addAll(Collection c)
   {
     return addAll(size, c);
   }
-  
+
+  /**
+   * Insert the elements of the collection in iteration order at the given
+   * index of this list. If this list is modified externally (for example,
+   * if this list is the collection), behavior is unspecified.
+   *
+   * @param c the collection to append
+   * @return true if the list was modified
+   * @throws NullPointerException if c is null
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   */
   public boolean addAll(int index, Collection c)
   {
-    if (index < 0 || index > size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
-    modCount++;
+    checkBoundsInclusive(index);
     int csize = c.size();
 
     if (csize == 0)
       return false;
 
     Iterator itr = c.iterator();
-    
+
     // Get the entries just before and after index. If index is at the start
-    // of the list, BEFORE is null. If index is at the end of thelist, AFTER is
-    // null. If the list is empty, both are null.
+    // of the list, BEFORE is null. If index is at the end of the list, AFTER
+    // is null. If the list is empty, both are null.
     Entry after = null;
-    Entry before = null;    
+    Entry before = null;
     if (index != size)
       {
-       after = getEntry(index);
-       before = after.previous;
+        after = getEntry(index);
+        before = after.previous;
       }
     else
       before = last;
-      
+
     // Create the first new entry. We do not yet set the link from `before'
-    // to the first entry, in order to deal with the case where (c == this). 
-    // [Actually, we don't have to handle this case to fufill the 
+    // to the first entry, in order to deal with the case where (c == this).
+    // [Actually, we don't have to handle this case to fufill the
     // contract for addAll(), but Sun's implementation appears to.]
     Entry e = new Entry(itr.next());
     e.previous = before;
     Entry prev = e;
     Entry firstNew = e;
-    
+
     // Create and link all the remaining entries.
     for (int pos = 1; pos < csize; pos++)
       {
         e = new Entry(itr.next());
-       e.previous = prev;      
-       prev.next = e;
-       prev = e;
+        e.previous = prev;
+        prev.next = e;
+        prev = e;
       }
+
     // Link the new chain of entries into the list.
+    modCount++;
+    size += csize;
     prev.next = after;
     if (after != null)
       after.previous = e;
     else
       last = e;
-    
+
     if (before != null)
       before.next = firstNew;
     else
       first = firstNew;
-    
-    size += csize;
     return true;
   }
 
+  /**
+   * Remove all elements from this list.
+   */
   public void clear()
   {
-    modCount++;
-    first = null;
-    last = null;
-    size = 0;
+    if (size > 0)
+      {
+        modCount++;
+        first = null;
+        last = null;
+        size = 0;
+      }
   }
 
+  /**
+   * Return the element at index.
+   *
+   * @param index the place to look
+   * @return the element at index
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
   public Object get(int index)
   {
-    if (index < 0 || index >= size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
-    Entry e = getEntry(index);
-    return e.data;
+    checkBoundsExclusive(index);
+    return getEntry(index).data;
   }
-  
+
+  /**
+   * Replace the element at the given location in the list.
+   *
+   * @param index which index to change
+   * @param o the new element
+   * @return the prior element
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
   public Object set(int index, Object o)
   {
-    if (index < 0 || index >= size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
+    checkBoundsExclusive(index);
     Entry e = getEntry(index);
     Object old = e.data;
     e.data = o;
     return old;
   }
 
+  /**
+   * Inserts an element in the given position in the list.
+   *
+   * @param index where to insert the element
+   * @param o the element to insert
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   */
   public void add(int index, Object o)
   {
-    if (index < 0 || index > size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
-    modCount++;
-    addEntry(index, new Entry(o));    
-  }
-  
-  private void addEntry(int index, Entry e)
-  {
+    checkBoundsInclusive(index);
+    Entry e = new Entry(o);
+
     if (index < size)
       {
-       Entry after = getEntry(index);
-       e.next = after;
-       e.previous = after.previous;
-       if (after.previous == null)
-         first = e;
-       else
-         after.previous.next = e;
-       after.previous = e;
-       size++;        
+        modCount++;
+        Entry after = getEntry(index);
+        e.next = after;
+        e.previous = after.previous;
+        if (after.previous == null)
+          first = e;
+        else
+          after.previous.next = e;
+        after.previous = e;
+        size++;
       }
     else
       addLastEntry(e);
   }
-  
+
+  /**
+   * Removes the element at the given position from the list.
+   *
+   * @param index the location of the element to remove
+   * @return the removed element
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   */
   public Object remove(int index)
   {
-    if (index < 0 || index >= size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
-    modCount++;
+    checkBoundsExclusive(index);
     Entry e = getEntry(index);
     removeEntry(e);
     return e.data;
   }
-  
+
+  /**
+   * Returns the first index where the element is located in the list, or -1.
+   *
+   * @param o the element to look for
+   * @return its position, or -1 if not found
+   */
   public int indexOf(Object o)
   {
     int index = 0;
     Entry e = first;
     while (e != null)
       {
-        if (e.data == null ? o == null : o.equals(e.data))
-         return index;
-       ++index;
+        if (equals(o, e.data))
+          return index;
+        index++;
         e = e.next;
       }
     return -1;
   }
-  
+
+  /**
+   * Returns the last index where the element is located in the list, or -1.
+   *
+   * @param o the element to look for
+   * @return its position, or -1 if not found
+   */
   public int lastIndexOf(Object o)
   {
     int index = size - 1;
     Entry e = last;
     while (e != null)
       {
-        if (e.data == null ? o == null : o.equals(e.data))
-         return index;
-       --index;
+        if (equals(o, e.data))
+          return index;
+        index--;
         e = e.previous;
       }
-    return -1;  
+    return -1;
   }
 
   /**
@@ -448,28 +618,27 @@ public class LinkedList extends AbstractSequentialList
    * methods.
    *
    * @param index the index of the element to be returned by the first call to
-   *   next(), or size() to be initially positioned at the end of the list.
-   * @exception IndexOutOfBoundsException if index < 0 || index > size().
+   *        next(), or size() to be initially positioned at the end of the list
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
    */
   public ListIterator listIterator(int index)
   {
-    if (index < 0 || index > size)
-      throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 
-                                          size);
+    checkBoundsInclusive(index);
     return new LinkedListItr(index);
   }
 
   /**
-   * Create a shallow copy of this LinkedList.
+   * Create a shallow copy of this LinkedList (the elements are not cloned).
+   *
    * @return an object of the same class as this object, containing the
-   * same elements in the same order.
+   *         same elements in the same order
    */
   public Object clone()
   {
     LinkedList copy = null;
     try
       {
-       copy = (LinkedList) super.clone();
+        copy = (LinkedList) super.clone();
       }
     catch (CloneNotSupportedException ex)
       {
@@ -478,7 +647,12 @@ public class LinkedList extends AbstractSequentialList
     copy.addAll(this);
     return copy;
   }
-  
+
+  /**
+   * Returns an array which contains the elements of the list in order.
+   *
+   * @return an array containing the list elements
+   */
   public Object[] toArray()
   {
     Object[] array = new Object[size];
@@ -490,182 +664,282 @@ public class LinkedList extends AbstractSequentialList
       }
     return array;
   }
-  
-  public Object[] toArray(Object[] array)
+
+  /**
+   * Returns an Array whose component type is the runtime component type of
+   * the passed-in Array.  The returned Array is populated with all of the
+   * elements in this LinkedList.  If the passed-in Array is not large enough
+   * to store all of the elements in this List, a new Array will be created 
+   * and returned; if the passed-in Array is <i>larger</i> than the size
+   * of this List, then size() index will be set to null.
+   *
+   * @param a the passed-in Array
+   * @return an array representation of this list
+   * @throws ArrayStoreException if the runtime type of a does not allow
+   *         an element in this list
+   * @throws NullPointerException if a is null
+   */
+  public Object[] toArray(Object[] a)
   {
-    if (array.length < size)
-      array = (Object[]) Array.newInstance(array.getClass().getComponentType(), 
-                                          size);
-    else if (array.length > size)
-      array[size] = null;
+    if (a.length < size)
+      a = (Object[]) Array.newInstance(a.getClass().getComponentType(), size);
+    else if (a.length > size)
+      a[size] = null;
     Entry e = first;
     for (int i = 0; i < size; i++)
       {
-        array[i] = e.data;
+        a[i] = e.data;
         e = e.next;
       }
-    return array;  
+    return a;
   }
 
   /**
-   * Serialize an object to a stream.
-   * @serialdata the size of the list (int), followed by all the elements
-   * (Object) in proper order.
+   * Serializes this object to the given stream.
+   *
+   * @param s the stream to write to
+   * @throws IOException if the underlying stream fails
+   * @serialData the size of the list (int), followed by all the elements
+   *             (Object) in proper order
    */
   private void writeObject(ObjectOutputStream s) throws IOException
   {
+    s.defaultWriteObject();
     s.writeInt(size);
-    Iterator itr = iterator();
-    for (int i = 0; i < size; i++)
-      s.writeObject(itr.next());
+    Entry e = first;
+    while (e != null)
+      {
+        s.writeObject(e.data);
+        e = e.next;
+      }
   }
 
   /**
-   * Deserialize an object from a stream.
-   * @serialdata the size of the list (int), followed by all the elements
-   * (Object) in proper order.
+   * Deserializes this object from the given stream.
+   *
+   * @param s the stream to read from
+   * @throws ClassNotFoundException if the underlying stream fails
+   * @throws IOException if the underlying stream fails
+   * @serialData the size of the list (int), followed by all the elements
+   *             (Object) in proper order
    */
   private void readObject(ObjectInputStream s)
     throws IOException, ClassNotFoundException
   {
-    int serialSize = s.readInt();
-    for (int i = 0; i < serialSize; i++)
+    s.defaultReadObject();
+    int i = s.readInt();
+    while (--i >= 0)
       addLastEntry(new Entry(s.readObject()));
   }
-  
-  /** A ListIterator over the list. This class keeps track of its
+
+  /**
+   * A ListIterator over the list. This class keeps track of its
    * position in the list and the two list entries it is between.
+   *
+   * @author Original author unknown
+   * @author Eric Blake <ebb9@email.byu.edu>
    */
-  class LinkedListItr implements ListIterator
+  private final class LinkedListItr implements ListIterator
   {
-    int knownMod;
-    Entry next;         // entry that will be returned by next().
-    Entry previous;     // entry that will be returned by previous().
-    Entry lastReturned; // entry that will be affected by remove() or set().
-    int position;       // index of `next'.
+    /** Number of modifications we know about. */
+    private int knownMod = modCount;
+
+    /** Entry that will be returned by next(). */
+    private Entry next;
+
+    /** Entry that will be returned by previous(). */
+    private Entry previous;
 
+    /** Entry that will be affected by remove() or set(). */
+    private Entry lastReturned;
+
+    /** Index of `next'. */
+    private int position;
+
+    /**
+     * Initialize the iterator.
+     *
+     * @param index the initial index
+     */
     LinkedListItr(int index)
     {
       if (index == size)
         {
           next = null;
-         previous = last;
-       }
+          previous = last;
+        }
       else
         {
           next = getEntry(index);
-         previous = next.previous;
-       }
+          previous = next.previous;
+        }
       position = index;
-      knownMod = modCount;
     }
 
+    /**
+     * Checks for iterator consistency.
+     *
+     * @throws ConcurrentModificationException if the list was modified
+     */
     private void checkMod()
     {
       if (knownMod != modCount)
-       throw new ConcurrentModificationException();
+        throw new ConcurrentModificationException();
     }
 
+    /**
+     * Returns the index of the next element.
+     *
+     * @return the next index
+     * @throws ConcurrentModificationException if the list was modified
+     */
     public int nextIndex()
     {
       checkMod();
       return position;
     }
 
+    /**
+     * Returns the index of the previous element.
+     *
+     * @return the previous index
+     * @throws ConcurrentModificationException if the list was modified
+     */
     public int previousIndex()
     {
       checkMod();
       return position - 1;
     }
 
+    /**
+     * Returns true if more elements exist via next.
+     *
+     * @return true if next will succeed
+     * @throws ConcurrentModificationException if the list was modified
+     */
     public boolean hasNext()
     {
       checkMod();
       return (next != null);
     }
 
+    /**
+     * Returns true if more elements exist via previous.
+     *
+     * @return true if previous will succeed
+     * @throws ConcurrentModificationException if the list was modified
+     */
     public boolean hasPrevious()
     {
       checkMod();
       return (previous != null);
     }
 
+    /**
+     * Returns the next element.
+     *
+     * @return the next element
+     * @throws ConcurrentModificationException if the list was modified
+     * @throws NoSuchElementException if there is no next
+     */
     public Object next()
     {
       checkMod();
       if (next == null)
-       throw new NoSuchElementException();
+        throw new NoSuchElementException();
       position++;
       lastReturned = previous = next;
       next = lastReturned.next;
       return lastReturned.data;
     }
 
+    /**
+     * Returns the previous element.
+     *
+     * @return the previous element
+     * @throws ConcurrentModificationException if the list was modified
+     * @throws NoSuchElementException if there is no previous
+     */
     public Object previous()
     {
       checkMod();
       if (previous == null)
-       throw new NoSuchElementException();
+        throw new NoSuchElementException();
       position--;
       lastReturned = next = previous;
       previous = lastReturned.previous;
       return lastReturned.data;
     }
 
+    /**
+     * Remove the most recently returned element from the list.
+     *
+     * @throws ConcurrentModificationException if the list was modified
+     * @throws IllegalStateException if there was no last element
+     */
     public void remove()
     {
       checkMod();
       if (lastReturned == null)
-       throw new IllegalStateException();
+        throw new IllegalStateException();
 
       // Adjust the position to before the removed element, if the element
       // being removed is behind the cursor.
       if (lastReturned == previous)
-       position--;
+        position--;
 
       next = lastReturned.next;
       previous = lastReturned.previous;
-      modCount++;
-      knownMod++;
       removeEntry(lastReturned);
-      
+      knownMod++;
+
       lastReturned = null;
     }
 
+    /**
+     * Adds an element between the previous and next, and advance to the next.
+     *
+     * @param o the element to add
+     * @throws ConcurrentModificationException if the list was modified
+     */
     public void add(Object o)
     {
       checkMod();
       modCount++;
       knownMod++;
+      size++;
+      position++;
       Entry e = new Entry(o);
       e.previous = previous;
       e.next = next;
 
       if (previous != null)
-       previous.next = e;
+        previous.next = e;
       else
-       first = e;
+        first = e;
 
       if (next != null)
-        {
-         next.previous = e;
-         next = next.next;
-       }
+        next.previous = e;
       else
-       last = e;
+        last = e;
 
       previous = e;
-      size++;
-      position++;
       lastReturned = null;
     }
 
+    /**
+     * Changes the contents of the element most recently returned.
+     *
+     * @param o the new element
+     * @throws ConcurrentModificationException if the list was modified
+     * @throws IllegalStateException if there was no last element
+     */
     public void set(Object o)
     {
       checkMod();
       if (lastReturned == null)
-       throw new IllegalStateException();
+        throw new IllegalStateException();
       lastReturned.data = o;
     }
-  }  // class LinkedListItr  
+  } // class LinkedListItr
 }
index f2ee49cd2606a1b662bd08ffe06af60b3be1cc5d..a000604de4974ba4126a7a2446bd9c91de1e6fca 100644 (file)
@@ -190,7 +190,7 @@ public interface List extends Collection
    * @see Object#equals(Object)
    * @see #hashCode()
    */
-  boolean equals(Object o);
+ /* boolean equals(Object o);*/
 
   /**
    * Get the element at a given index in this list.
@@ -288,7 +288,7 @@ public interface List extends Collection
   Object remove(int index);
 
   /**
-   * Remove the first occurrence of an object from this list (optional
+   * Remove the first occurence of an object from this list (optional
    * operation). That is, remove the first element e such that
    * <code>o == null ? e == null : o.equals(e)</code>.
    *
index 8647fa48b087e3c266aa82675cdd0aeec9281a91..a7b3f5d4e705ebba630f2c6e1a908c7449547606 100644 (file)
@@ -32,24 +32,30 @@ package java.util;
  * "The Java Language Specification", ISBN 0-201-63451-1
  * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
  * Status:  Believed complete and correct
- */
 
 /**
  * Stack provides a Last In First Out (LIFO) data type, commonly known
- * as a Stack.  
- *
- * Stack itself extends Vector and provides the additional methods
- * for stack manipulation (push, pop, peek). 
+ * as a Stack.  Stack itself extends Vector and provides the additional
+ * methods for stack manipulation (push, pop, peek). You can also seek for
+ * the 1-based position of an element on the stack.
  *
  * @author Warren Levy <warrenl@cygnus.com>
- * @date August 20, 1998.
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see List
+ * @see AbstractList
+ * @see LinkedList
+ * @since 1.0
+ * @status updated to 1.4
  */
 public class Stack extends Vector
 {
-  // Could use Vector methods internally for the following methods
+  // We could use Vector methods internally for the following methods,
   // but have used Vector fields directly for efficiency (i.e. this
   // often reduces out duplicate bounds checking).
 
+  /**
+   * Compatible with JDK 1.0+.
+   */
   private static final long serialVersionUID = 1224463164541339165L;
 
   /**
@@ -57,16 +63,15 @@ public class Stack extends Vector
    */
   public Stack()
   {
-    super();
   }
 
   /**
    * Pushes an Object onto the top of the stack.  This method is effectively
-   * the same as addElement(item)
+   * the same as addElement(item).
    *
    * @param item the Object to push onto the stack
-   * @returns the Object pushed onto the stack
-   * @see java.util.Vector#addElement(java.util.Object)
+   * @return the Object pushed onto the stack
+   * @see Vector#addElement(Object)
    */
   public Object push(Object item)
   {
@@ -80,26 +85,29 @@ public class Stack extends Vector
 
   /**
    * Pops an item from the stack and returns it.  The item popped is
-   * removed from the Stack
+   * removed from the Stack.
    *
-   * @returns the Object popped from the stack
+   * @return the Object popped from the stack
+   * @throws EmptyStackException if the stack is empty
    */
   public synchronized Object pop()
   {
     if (elementCount == 0)
       throw new EmptyStackException();
 
+    modCount++;
     Object obj = elementData[--elementCount];
 
-    // Set topmost element to null to assist the gc in cleanup
+    // Set topmost element to null to assist the gc in cleanup.
     elementData[elementCount] = null;
     return obj;
   }
 
   /**
-   * Returns the top Object on the stack without removing it
+   * Returns the top Object on the stack without removing it.
    *
-   * @returns the top Object on the stack
+   * @return the top Object on the stack
+   * @throws EmptyStackException if the stack is empty
    */
   public synchronized Object peek()
   {
@@ -110,11 +118,11 @@ public class Stack extends Vector
   }
 
   /**
-   * Tests if the stack is empty
+   * Tests if the stack is empty.
    *
-   * @returns true if the stack contains no items, false otherwise
+   * @return true if the stack contains no items, false otherwise
    */
-  public boolean empty()
+  public synchronized boolean empty()
   {
     return elementCount == 0;
   }
@@ -122,18 +130,18 @@ public class Stack extends Vector
   /**
    * Returns the position of an Object on the stack, with the top
    * most Object being at position 1, and each Object deeper in the
-   * stack at depth + 1
+   * stack at depth + 1.
    *
    * @param o The object to search for
-   * @returns The 1 based depth of the Object, or -1 if the Object 
-   * is not on the stack.
+   * @return The 1 based depth of the Object, or -1 if the Object
+   *         is not on the stack
    */
   public synchronized int search(Object o)
   {
-    for (int i = elementCount-1; i >=0; --i)
-      if (elementData[i].equals(o))
+    int i = elementCount;
+    while (--i >= 0)
+      if (equals(o, elementData[i]))
         return elementCount - i;
-
     return -1;
   }
 }
index 59d6079e32ae50c70ade786a60444af52fe80385..83386d6e54b6b646acb7169cef0a676ac88715a9 100644 (file)
@@ -38,80 +38,166 @@ import java.io.IOException;
  * interface.  Elements in the Map will be sorted by either a user-provided
  * Comparator object, or by the natural ordering of the keys.
  *
- * The algorithms are adopted from Corman, Leiserson,
- * and Rivest's <i>Introduction to Algorithms.</i>  In other words,
- * I cribbed from the same pseudocode as Sun.  <em>Any similarity
- * between my code and Sun's (if there is any -- I have never looked
- * at Sun's) is a result of this fact.</em>
- *
- * TreeMap guarantees O(log n) insertion and deletion of elements.  That 
- * being said, there is a large enough constant coefficient in front of 
- * that "log n" (overhead involved in keeping the tree 
- * balanced), that TreeMap may not be the best choice for small
- * collections.
+ * The algorithms are adopted from Corman, Leiserson, and Rivest's
+ * <i>Introduction to Algorithms.</i>  TreeMap guarantees O(log n)
+ * insertion and deletion of elements.  That being said, there is a large
+ * enough constant coefficient in front of that "log n" (overhead involved
+ * in keeping the tree balanced), that TreeMap may not be the best choice
+ * for small collections. If something is already sorted, you may want to
+ * just use a LinkedHashMap to maintain the order while providing O(1) access.
  *
  * TreeMap is a part of the JDK1.2 Collections API.  Null keys are allowed
- * only if a Comparator is used which can deal with them.  Null values are 
- * always allowed.
+ * only if a Comparator is used which can deal with them; natural ordering
+ * cannot cope with null.  Null values are always allowed. Note that the
+ * ordering must be <i>consistent with equals</i> to correctly implement
+ * the Map interface. If this condition is violated, the map is still
+ * well-behaved, but you may have suprising results when comparing it to
+ * other maps.<p>
+ *
+ * This implementation is not synchronized. If you need to share this between
+ * multiple threads, do something like:<br>
+ * <code>SortedMap m
+ *       = Collections.synchronizedSortedMap(new TreeMap(...));</code><p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * <code>ConcurrentModificationException</code> rather than exhibit
+ * non-deterministic behavior.
  *
- * @author           Jon Zeppieri
- * @author          Bryce McKinlay
+ * @author Jon Zeppieri
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Map
+ * @see HashMap
+ * @see Hashtable
+ * @see LinkedHashMap
+ * @see Comparable
+ * @see Comparator
+ * @see Collection
+ * @see Collections#synchronizedSortedMap(SortedMap)
+ * @since 1.2
+ * @status updated to 1.4
  */
 public class TreeMap extends AbstractMap
   implements SortedMap, Cloneable, Serializable
 {
-  private static final int RED = -1,
-                           BLACK = 1;
+  // Implementation note:
+  // A red-black tree is a binary search tree with the additional properties
+  // that all paths to a leaf node visit the same number of black nodes,
+  // and no red node has red children. To avoid some null-pointer checks,
+  // we use the special node nil which is always black, has no relatives,
+  // and has key and value of null (but is not equal to a mapping of null).
 
-  /** Sentinal node, used to avoid null checks for corner cases and make the
-      delete rebalance code simpler. Note that this must not be static, due 
-      to thread-safety concerns. */
-  transient Node nil = new Node(null, null);
+  /**
+   * Compatible with JDK 1.2.
+   */
+  private static final long serialVersionUID = 919286545866124006L;
 
-  /** The root node of this TreeMap */
-  transient Node root = nil;
+  /**
+   * Color status of a node. Package visible for use by nested classes.
+   */
+  static final int RED = -1,
+                   BLACK = 1;
 
-  /** The size of this TreeMap */
-  transient int size = 0;
+  /**
+   * Sentinal node, used to avoid null checks for corner cases and make the
+   * delete rebalance code simpler. The rebalance code must never assign
+   * the parent, left, or right of nil, but may safely reassign the color
+   * to be black. This object must never be used as a key in a TreeMap, or
+   * it will break bounds checking of a SubMap.
+   */
+  static final Node nil = new Node(null, null, BLACK);
+  static
+    {
+      // Nil is self-referential, so we must initialize it after creation.
+      nil.parent = nil;
+      nil.left = nil;
+      nil.right = nil;
+    }
 
-  /** Number of modifications */
-  transient int modCount = 0;
+  /**
+   * The root node of this TreeMap.
+   */
+  private transient Node root = nil;
+
+  /**
+   * The size of this TreeMap. Package visible for use by nested classes.
+   */
+  transient int size;
 
-  /** This TreeMap's comparator, if any. */
-  Comparator comparator = null;
+  /**
+   * The cache for {@link #entrySet()}.
+   */
+  private transient Set entries;
 
-  static final long serialVersionUID = 919286545866124006L;
+  /**
+   * Counts the number of modifications this TreeMap has undergone, used
+   * by Iterators to know when to throw ConcurrentModificationExceptions.
+   * Package visible for use by nested classes.
+   */
+  transient int modCount;
+
+  /**
+   * This TreeMap's comparator, or null for natural ordering.
+   * Package visible for use by nested classes.
+   * @serial the comparator ordering this tree, or null
+   */
+  final Comparator comparator;
 
-  private static class Node extends BasicMapEntry implements Map.Entry
+  /**
+   * Class to represent an entry in the tree. Holds a single key-value pair,
+   * plus pointers to parent and child nodes.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class Node extends BasicMapEntry
   {
+    // All fields package visible for use by nested classes.
+    /** The color of this node. */
     int color;
-    Node left;
-    Node right;
-    Node parent;
 
-    Node(Object key, Object value)
+    /** The left child node. */
+    Node left = nil;
+    /** The right child node. */
+    Node right = nil;
+    /** The parent node. */
+    Node parent = nil;
+
+    /**
+     * Simple constructor.
+     * @param key the key
+     * @param value the value
+     */
+    Node(Object key, Object value, int color)
     {
       super(key, value);
-      this.color = BLACK;
+      this.color = color;
     }
   }
 
   /**
-   * Instantiate a new TreeMap with no elements, using the keys'
-   * natural ordering to sort.
+   * Instantiate a new TreeMap with no elements, using the keys' natural
+   * ordering to sort. All entries in the map must have a key which implements
+   * Comparable, and which are <i>mutually comparable</i>, otherwise map
+   * operations may throw a {@link ClassCastException}. Attempts to use
+   * a null key will throw a {@link NullPointerException}.
    *
-   * @see java.lang.Comparable
+   * @see Comparable
    */
   public TreeMap()
   {
+    this((Comparator) null);
   }
 
   /**
-   * Instantiate a new TreeMap with no elements, using the provided
-   * comparator to sort.
+   * Instantiate a new TreeMap with no elements, using the provided comparator
+   * to sort. All entries in the map must have keys which are mutually
+   * comparable by the Comparator, otherwise map operations may throw a
+   * {@link ClassCastException}.
    *
-   * @param        oComparator        a Comparator object, used to sort 
-   *                                  the keys of this SortedMap
+   * @param comparator the sort order for the keys of this map, or null
+   *        for the natural order
    */
   public TreeMap(Comparator c)
   {
@@ -119,62 +205,70 @@ public class TreeMap extends AbstractMap
   }
 
   /**
-   * Instantiate a new TreeMap, initializing it with all of the
-   * elements in the provided Map.  The elements will be sorted 
-   * using the natural ordering of the keys.
-   *
-   * @param              map         a Map, whose keys will be put into
-   *                                  this TreeMap
+   * Instantiate a new TreeMap, initializing it with all of the elements in
+   * the provided Map.  The elements will be sorted using the natural
+   * ordering of the keys. This algorithm runs in n*log(n) time. All entries
+   * in the map must have keys which implement Comparable and are mutually
+   * comparable, otherwise map operations may throw a
+   * {@link ClassCastException}.
    *
-   * @throws             ClassCastException     if the keys in the provided
-   *                                            Map do not implement 
-   *                                            Comparable
-   *
-   * @see                java.lang.Comparable
+   * @param map a Map, whose entries will be put into this TreeMap
+   * @throws ClassCastException if the keys in the provided Map are not
+   *         comparable
+   * @throws NullPointerException if map is null
+   * @see Comparable
    */
   public TreeMap(Map map)
   {
+    this((Comparator) null);
     putAll(map);
   }
 
-  /** 
-   * Instantiate a new TreeMap, initializing it with all of the
-   * elements in the provided SortedMap.  The elements will be sorted 
-   * using the same method as in the provided SortedMap.
+  /**
+   * Instantiate a new TreeMap, initializing it with all of the elements in
+   * the provided SortedMap.  The elements will be sorted using the same
+   * comparator as in the provided SortedMap. This runs in linear time.
+   *
+   * @param sm a SortedMap, whose entries will be put into this TreeMap
+   * @throws NullPointerException if sm is null
    */
   public TreeMap(SortedMap sm)
   {
     this(sm.comparator());
-
-    int sm_size = sm.size();
+    int pos = sm.size();
     Iterator itr = sm.entrySet().iterator();
 
-    fabricateTree(sm_size);
+    fabricateTree(pos);
     Node node = firstNode();
-    
-    for (int i = 0; i < sm_size; i++)
+
+    while (--pos >= 0)
       {
-       Map.Entry me = (Map.Entry) itr.next();
-       node.key = me.getKey();
-       node.value = me.getValue();     
-       node = successor(node);
+        Map.Entry me = (Map.Entry) itr.next();
+        node.key = me.getKey();
+        node.value = me.getValue();
+        node = successor(node);
       }
   }
 
-  public int size()
-  {
-    return size;
-  }
-
+  /**
+   * Clears the Map so it has no keys. This is O(1).
+   */
   public void clear()
   {
-    modCount++;
-    root = nil;
-    // nil node could have a residual parent reference, clear it for GC.
-    nil.parent = null;
-    size = 0;
+    if (size > 0)
+      {
+        modCount++;
+        root = nil;
+        size = 0;
+      }
   }
 
+  /**
+   * Returns a shallow clone of this TreeMap. The Map itself is cloned,
+   * but its contents are not.
+   *
+   * @return the clone
+   */
   public Object clone()
   {
     TreeMap copy = null;
@@ -185,547 +279,805 @@ public class TreeMap extends AbstractMap
     catch (CloneNotSupportedException x)
       {
       }
-    // Each instance must have a unique sentinal.
-    copy.nil = new Node(null, null);
+    copy.entries = null;
     copy.fabricateTree(size);
 
     Node node = firstNode();
     Node cnode = copy.firstNode();
-    
+
     while (node != nil)
       {
         cnode.key = node.key;
-       cnode.value = node.value;
-       node = successor(node);
-       cnode = copy.successor(cnode);
+        cnode.value = node.value;
+        node = successor(node);
+        cnode = copy.successor(cnode);
       }
     return copy;
   }
-  
+
+  /**
+   * Return the comparator used to sort this map, or null if it is by
+   * natural order.
+   *
+   * @return the map's comparator
+   */
   public Comparator comparator()
   {
     return comparator;
   }
 
+  /**
+   * Returns true if the map contains a mapping for the given key.
+   *
+   * @param key the key to look for
+   * @return true if the key has a mapping
+   * @throws ClassCastException if key is not comparable to map elements
+   * @throws NullPointerException if key is null and the comparator is not
+   *         tolerant of nulls
+   */
   public boolean containsKey(Object key)
   {
     return getNode(key) != nil;
   }
 
+  /**
+   * Returns true if the map contains at least one mapping to the given value.
+   * This requires linear time.
+   *
+   * @param value the value to look for
+   * @return true if the value appears in a mapping
+   */
   public boolean containsValue(Object value)
   {
     Node node = firstNode();
-    Object currentVal;
-
     while (node != nil)
       {
-       currentVal = node.getValue();
-
-        if (value == null ? currentVal == null : value.equals (currentVal))
-         return true;
-
-       node = successor(node);
+        if (equals(value, node.value))
+          return true;
+        node = successor(node);
       }
     return false;
   }
 
+  /**
+   * Returns a "set view" of this TreeMap's entries. The set is backed by
+   * the TreeMap, so changes in one show up in the other.  The set supports
+   * element removal, but not element addition.<p>
+   *
+   * Note that the iterators for all three views, from keySet(), entrySet(),
+   * and values(), traverse the TreeMap in sorted sequence.
+   *
+   * @return a set view of the entries
+   * @see #keySet()
+   * @see #values()
+   * @see Map.Entry
+   */
   public Set entrySet()
   {
-    // Create an AbstractSet with custom implementations of those methods that 
-    // can be overriden easily and efficiently.
-    return new AbstractSet()
-    {
-      public int size()
-      {
-        return size;
-      }
-      
-      public Iterator iterator()
-      {
-        return new TreeIterator(TreeIterator.ENTRIES);
-      }
-            
-      public void clear()
+    if (entries == null)
+      // Create an AbstractSet with custom implementations of those methods
+      // that can be overriden easily and efficiently.
+      entries = new AbstractSet()
       {
-        TreeMap.this.clear();
-      }
+        public int size()
+        {
+          return size;
+        }
 
-      public boolean contains(Object o)
-      {
-        if (!(o instanceof Map.Entry))
-         return false;
-       Map.Entry me = (Map.Entry) o;
-       Node n = getNode(me.getKey());
-       return (n != nil && me.getValue().equals(n.value));
-      }
-      
-      public boolean remove(Object o)
-      {
-        if (!(o instanceof Map.Entry))
-         return false;
-       Map.Entry me = (Map.Entry) o;
-       Node n = getNode(me.getKey());
-       if (n != nil && me.getValue().equals(n.value))
-         {
-           removeNode(n);
-           return true;
-         }
-       return false;
+        public Iterator iterator()
+        {
+          return new TreeIterator(ENTRIES);
+        }
+
+        public void clear()
+        {
+          TreeMap.this.clear();
+        }
+
+        public boolean contains(Object o)
+        {
+          if (! (o instanceof Map.Entry))
+            return false;
+          Map.Entry me = (Map.Entry) o;
+          Node n = getNode(me.getKey());
+          return n != nil && AbstractSet.equals(me.getValue(), n.value);
       }
-    };
+
+        public boolean remove(Object o)
+        {
+          if (! (o instanceof Map.Entry))
+            return false;
+          Map.Entry me = (Map.Entry) o;
+          Node n = getNode(me.getKey());
+          if (n != nil && AbstractSet.equals(me.getValue(), n.value))
+            {
+              removeNode(n);
+              return true;
+            }
+          return false;
+        }
+      };
+    return entries;
   }
 
+  /**
+   * Returns the first (lowest) key in the map.
+   *
+   * @return the first key
+   * @throws NoSuchElementException if the map is empty
+   */
   public Object firstKey()
   {
     if (root == nil)
-      throw new NoSuchElementException("empty");
-    return firstNode().getKey();
-  }
-  
-  private Node firstNode()
-  {
-    if (root == nil)
-      return nil;
-    Node node = root;
-    while (node.left != nil)
-      node = node.left;
-    return node;
+      throw new NoSuchElementException();
+    return firstNode().key;
   }
 
-  public Object lastKey()
-  {
-    if (root == nil)
-      throw new NoSuchElementException("empty");
-    return lastNode().getKey();
-  }
-  
-  private Node lastNode()
-  {
-    if (root == nil)
-      return nil;
-    Node node = root;
-    while (node.right != nil)
-      node = node.right;
-    return node;  
-  }
-  
+  /**
+   * Return the value in this TreeMap associated with the supplied key,
+   * or <code>null</code> if the key maps to nothing.  NOTE: Since the value
+   * could also be null, you must use containsKey to see if this key
+   * actually maps to something.
+   *
+   * @param key the key for which to fetch an associated value
+   * @return what the key maps to, if present
+   * @throws ClassCastException if key is not comparable to elements in the map
+   * @throws NullPointerException if key is null but the comparator does not
+   *         tolerate nulls
+   * @see #put(Object, Object)
+   * @see #containsKey(Object)
+   */
   public Object get(Object key)
   {
+    // Exploit fact that nil.value == null.
     return getNode(key).value;
   }
-  
-  /** Return the TreeMap.Node associated with KEY, or the nil node if no such
-      node exists in the tree. */
-  private Node getNode(Object key)
-  {
-    int comparison;
-    Node current = root;
 
-    while (current != nil)
-      {
-        comparison = compare(key, current.key);
-       if (comparison > 0)
-         current = current.right;
-       else if (comparison < 0)
-         current = current.left;
-       else
-         return current;
-      }
-    return current; 
+  /**
+   * Returns a view of this Map including all entries with keys less than
+   * <code>toKey</code>. The returned map is backed by the original, so changes
+   * in one appear in the other. The submap will throw an
+   * {@link IllegalArgumentException} for any attempt to access or add an
+   * element beyond the specified cutoff. The returned map does not include
+   * the endpoint; if you want inclusion, pass the successor element.
+   *
+   * @param toKey the (exclusive) cutoff point
+   * @return a view of the map less than the cutoff
+   * @throws ClassCastException if <code>toKey</code> is not compatible with
+   *         the comparator (or is not Comparable, for natural ordering)
+   * @throws NullPointerException if toKey is null, but the comparator does not
+   *         tolerate null elements
+   */
+  public SortedMap headMap(Object toKey)
+  {
+    return new SubMap(nil, toKey);
   }
 
+  /**
+   * Returns a "set view" of this TreeMap's keys. The set is backed by the
+   * TreeMap, so changes in one show up in the other.  The set supports
+   * element removal, but not element addition.
+   *
+   * @return a set view of the keys
+   * @see #values()
+   * @see #entrySet()
+   */
   public Set keySet()
   {
-    // Create an AbstractSet with custom implementations of those methods that 
-    // can be overriden easily and efficiently.
-    return new AbstractSet()
-    {
-      public int size()
+    if (keys == null)
+      // Create an AbstractSet with custom implementations of those methods
+      // that can be overriden easily and efficiently.
+      keys = new AbstractSet()
       {
-        return size;
-      }
-      
-      public Iterator iterator()
-      {
-        return new TreeIterator(TreeIterator.KEYS);
-      }
+        public int size()
+        {
+          return size;
+        }
 
-      public void clear()
-      {
-        TreeMap.this.clear();
-      }
+        public Iterator iterator()
+        {
+          return new TreeIterator(KEYS);
+        }
 
-      public boolean contains(Object o)
-      {
-        return TreeMap.this.containsKey(o);
-      }
-      
-      public boolean remove(Object key)
-      {
-        Node n = getNode(key);
-       if (n == nil)
-         return false;
-        TreeMap.this.removeNode(n);
-       return true;
-      }
-    };
+        public void clear()
+        {
+          TreeMap.this.clear();
+        }
+
+        public boolean contains(Object o)
+        {
+          return containsKey(o);
+        }
+
+        public boolean remove(Object key)
+        {
+          Node n = getNode(key);
+          if (n == nil)
+            return false;
+          removeNode(n);
+          return true;
+        }
+      };
+    return keys;
+  }
+
+  /**
+   * Returns the last (highest) key in the map.
+   *
+   * @return the last key
+   * @throws NoSuchElementException if the map is empty
+   */
+  public Object lastKey()
+  {
+    if (root == nil)
+      throw new NoSuchElementException("empty");
+    return lastNode().key;
   }
 
+  /**
+   * Puts the supplied value into the Map, mapped by the supplied key.
+   * The value may be retrieved by any object which <code>equals()</code>
+   * this key. NOTE: Since the prior value could also be null, you must
+   * first use containsKey if you want to see if you are replacing the
+   * key's mapping.
+   *
+   * @param key the key used to locate the value
+   * @param value the value to be stored in the HashMap
+   * @return the prior mapping of the key, or null if there was none
+   * @throws ClassCastException if key is not comparable to current map keys
+   * @throws NullPointerException if key is null, but the comparator does
+   *         not tolerate nulls
+   * @see #get(Object)
+   * @see Object#equals(Object)
+   */
   public Object put(Object key, Object value)
   {
-    modCount++;
     Node current = root;
     Node parent = nil;
     int comparison = 0;
-    
+
     // Find new node's parent.
     while (current != nil)
       {
-       parent = current;
-       comparison = compare(key, current.key);
-       if (comparison > 0)
-         current = current.right;
-       else if (comparison < 0)
-         current = current.left;
-       else
-         {
-           // Key already in tree.
-           Object r = current.value;
-           current.value = value;
-           return r;
-         }
+        parent = current;
+        comparison = compare(key, current.key);
+        if (comparison > 0)
+          current = current.right;
+        else if (comparison < 0)
+          current = current.left;
+        else // Key already in tree.
+          return current.setValue(value);
       }
-    
+
     // Set up new node.
-    Node n = new Node(key, value);
-    n.color = RED;
+    Node n = new Node(key, value, RED);
     n.parent = parent;
-    n.left = nil;
-    n.right = nil;
-    
+
     // Insert node in tree.
+    modCount++;
     size++;
     if (parent == nil)
       {
-        // Special case: inserting into an empty tree.
-       root = n;
-       n.color = BLACK;
-       return null;
+        // Special case inserting into an empty tree.
+        root = n;
+        return null;
       }
-    else if (comparison > 0)
+    if (comparison > 0)
       parent.right = n;
     else
-      parent.left = n;   
-    
+      parent.left = n;
+
     // Rebalance after insert.
     insertFixup(n);
-    //verifyTree();
     return null;
   }
 
-  /** Maintain red-black balance after inserting a new node. */
-  private void insertFixup(Node n)
-  {
-    // Only need to rebalance when parent is a RED node, and while at least
-    // 2 levels deep into the tree (ie: node has a grandparent).
-    while (n != root && n.parent.parent != nil && n.parent.color == RED)
-      {
-       if (n.parent == n.parent.parent.left)
-         {
-            Node uncle = n.parent.parent.right;
-            if (uncle != nil && uncle.color == RED) 
-             {
-               n.parent.color = BLACK;
-               uncle.color = BLACK;
-               n.parent.parent.color = RED;
-               n = n.parent.parent;
-              }
-           else // Uncle is BLACK.
-             {                
-                if (n == n.parent.right)
-                 {
-                    // Make n a left child.
-                    n = n.parent;
-                    rotateLeft(n);
-                  }
-
-                // Recolor and rotate.
-                n.parent.color = BLACK;
-                n.parent.parent.color = RED;
-                rotateRight(n.parent.parent);
-              }
-         }
-       else
-         {
-           // Mirror image of above code.
-           Node uncle = n.parent.parent.left;
-            if (uncle != nil && uncle.color == RED)
-             {
-                n.parent.color = BLACK;
-                uncle.color = BLACK;
-                n.parent.parent.color = RED;
-                n = n.parent.parent;
-              }
-           else
-             {
-                if (n == n.parent.left)
-                 {
-                    n = n.parent;
-                    rotateRight(n);
-                  }
-                n.parent.color = BLACK;
-                n.parent.parent.color = RED;
-                rotateLeft(n.parent.parent);
-             }
-         }
-      }
-    root.color = BLACK;
-  }
-
+  /**
+   * Copies all elements of the given map into this hashtable.  If this table
+   * already has a mapping for a key, the new mapping replaces the current
+   * one.
+   *
+   * @param m the map to be hashed into this
+   * @throws ClassCastException if a key in m is not comparable with keys
+   *         in the map
+   * @throws NullPointerException if a key in m is null, and the comparator
+   *         does not tolerate nulls
+   */
   public void putAll(Map m)
   {
     Iterator itr = m.entrySet().iterator();
-    int msize = m.size();
-    Map.Entry e;
-
-    for (int i = 0; i < msize; i++)
+    int pos = m.size();
+    while (--pos >= 0)
       {
-       e = (Map.Entry) itr.next();
-       put(e.getKey(), e.getValue());
+        Map.Entry e = (Map.Entry) itr.next();
+        put(e.getKey(), e.getValue());
       }
   }
 
+  /**
+   * Removes from the TreeMap and returns the value which is mapped by the
+   * supplied key. If the key maps to nothing, then the TreeMap remains
+   * unchanged, and <code>null</code> is returned. NOTE: Since the value
+   * could also be null, you must use containsKey to see if you are
+   * actually removing a mapping.
+   *
+   * @param key the key used to locate the value to remove
+   * @return whatever the key mapped to, if present
+   * @throws ClassCastException if key is not comparable to current map keys
+   * @throws NullPointerException if key is null, but the comparator does
+   *         not tolerate nulls
+   */
   public Object remove(Object key)
   {
     Node n = getNode(key);
-    if (n != nil)
-      {
-        removeNode(n);
-       return n.value;
-      }
-    return null;
+    if (n == nil)
+      return null;
+    removeNode(n);
+    return n.value;
   }
-  
-  // Remove node from tree. This will increment modCount and decrement size. 
-  // Node must exist in the tree.
-  private void removeNode(Node node) // z
+
+  /**
+   * Returns the number of key-value mappings currently in this Map.
+   *
+   * @return the size
+   */
+  public int size()
   {
-    Node splice; // y
-    Node child;  // x
-    
-    modCount++;
-    size--;
+    return size;
+  }
 
-    // Find splice, the node at the position to actually remove from the tree. 
-    if (node.left == nil || node.right == nil)
-      {
-       // Node to be deleted has 0 or 1 children.
-        splice = node;
-       if (node.left == nil)
-         child = node.right;
-       else
-         child = node.left;
-      }
-    else
-      {
-       // Node has 2 children. Splice is node's successor, and will be 
-       // swapped with node since we can't remove node directly.
-        splice = node.right;
-        while (splice.left != nil)
-         splice = splice.left;
-       child = splice.right;
-      }
+  /**
+   * Returns a view of this Map including all entries with keys greater or
+   * equal to <code>fromKey</code> and less than <code>toKey</code> (a
+   * half-open interval). The returned map is backed by the original, so
+   * changes in one appear in the other. The submap will throw an
+   * {@link IllegalArgumentException} for any attempt to access or add an
+   * element beyond the specified cutoffs. The returned map includes the low
+   * endpoint but not the high; if you want to reverse this behavior on
+   * either end, pass in the successor element.
+   *
+   * @param fromKey the (inclusive) low cutoff point
+   * @param toKey the (exclusive) high cutoff point
+   * @return a view of the map between the cutoffs
+   * @throws ClassCastException if either cutoff is not compatible with
+   *         the comparator (or is not Comparable, for natural ordering)
+   * @throws NullPointerException if fromKey or toKey is null, but the
+   *         comparator does not tolerate null elements
+   * @throws IllegalArgumentException if fromKey is greater than toKey
+   */
+  public SortedMap subMap(Object fromKey, Object toKey)
+  {
+    return new SubMap(fromKey, toKey);
+  }
 
-    // Unlink splice from the tree.
-    Node parent = splice.parent;
-    child.parent = parent;
-    if (parent != nil)
+  /**
+   * Returns a view of this Map including all entries with keys greater or
+   * equal to <code>fromKey</code>. The returned map is backed by the
+   * original, so changes in one appear in the other. The submap will throw an
+   * {@link IllegalArgumentException} for any attempt to access or add an
+   * element beyond the specified cutoff. The returned map includes the
+   * endpoint; if you want to exclude it, pass in the successor element.
+   *
+   * @param fromKey the (inclusive) low cutoff point
+   * @return a view of the map above the cutoff
+   * @throws ClassCastException if <code>fromKey</code> is not compatible with
+   *         the comparator (or is not Comparable, for natural ordering)
+   * @throws NullPointerException if fromKey is null, but the comparator
+   *         does not tolerate null elements
+   */
+  public SortedMap tailMap(Object fromKey)
+  {
+    return new SubMap(fromKey, nil);
+  }
+
+  /**
+   * Returns a "collection view" (or "bag view") of this TreeMap's values.
+   * The collection is backed by the TreeMap, so changes in one show up
+   * in the other.  The collection supports element removal, but not element
+   * addition.
+   *
+   * @return a bag view of the values
+   * @see #keySet()
+   * @see #entrySet()
+   */
+  public Collection values()
+  {
+    if (values == null)
+      // We don't bother overriding many of the optional methods, as doing so
+      // wouldn't provide any significant performance advantage.
+      values = new AbstractCollection()
       {
-       if (splice == parent.left)
-          parent.left = child;
-       else
-          parent.right = child;
-      }
-    else
-      root = child;
+        public int size()
+        {
+          return size;
+        }
 
-    // Keep track of splice's color in case it gets changed in the swap.
-    int spliceColor = splice.color;
+        public Iterator iterator()
+        {
+          return new TreeIterator(VALUES);
+        }
 
-/*
-    if (splice != node)
-      {
-        node.key = splice.key;
-       node.value = splice.value;
-      }
-*/
-    if (splice != node)
-      {
-        // Swap SPLICE for NODE. Some implementations optimize here by simply
-       // swapping the values, but we can't do that: if an iterator was
-       // referencing a node in its "next" field, and that node got swapped, 
-       // things would get confused.
-       if (node == root)
-         {
-           root = splice;
-         }
-       else
-         {
-           if (node.parent.left == node)
-             node.parent.left = splice;
-           else
-             node.parent.right = splice;
-          }
-       splice.parent = node.parent;
-       splice.left = node.left;
-       splice.right = node.right;
-       splice.left.parent = splice;
-       splice.right.parent = splice;
-       splice.color = node.color;
-      }
+        public void clear()
+        {
+          TreeMap.this.clear();
+        }
+      };
+    return values;
+  }
 
-    if (spliceColor == BLACK)
-      deleteFixup (child);
-    
-    //verifyTree();      
+  /**
+   * Compares two elements by the set comparator, or by natural ordering.
+   * Package visible for use by nested classes.
+   *
+   * @param o1 the first object
+   * @param o2 the second object
+   * @throws ClassCastException if o1 and o2 are not mutually comparable,
+   *         or are not Comparable with natural ordering
+   * @throws NullPointerException if o1 or o2 is null with natural ordering
+   */
+  final int compare(Object o1, Object o2)
+  {
+    return (comparator == null
+            ? ((Comparable) o1).compareTo(o2)
+            : comparator.compare(o1, o2));
   }
 
-  /** Maintain red-black balance after deleting a node. */
-  private void deleteFixup (Node node)
+  /**
+   * Maintain red-black balance after deleting a node.
+   *
+   * @param node the child of the node just deleted, possibly nil
+   * @param parent the parent of the node just deleted, never nil
+   */
+  private void deleteFixup(Node node, Node parent)
   {
-    // A black node has been removed, so we need to rebalance to avoid 
+    // if (parent == nil)
+    //   throw new InternalError();
+    // If a black node has been removed, we need to rebalance to avoid
     // violating the "same number of black nodes on any path" rule. If
-    // node is red, we can simply recolor it black and all is well. 
+    // node is red, we can simply recolor it black and all is well.
     while (node != root && node.color == BLACK)
       {
-        if (node == node.parent.left)
-         {
-           // Rebalance left side.
-           Node sibling = node.parent.right;
-           if (sibling.color == RED)
-             {
+        if (node == parent.left)
+          {
+            // Rebalance left side.
+            Node sibling = parent.right;
+            // if (sibling == nil)
+            //   throw new InternalError();
+            if (sibling.color == RED)
+              {
+                // Case 1: Sibling is red.
+                // Recolor sibling and parent, and rotate parent left.
                 sibling.color = BLACK;
-                node.parent.color = RED;
-                rotateLeft(node.parent);
-                sibling = node.parent.right;
-             }
+                parent.color = RED;
+                rotateLeft(parent);
+                sibling = parent.right;
+              }
 
-           if (sibling.left.color == BLACK && sibling.right.color == BLACK)
+            if (sibling.left.color == BLACK && sibling.right.color == BLACK)
               {
-               // Case 2: Sibling has no red children.
-               sibling.color = RED;
-               // Black height has been decreased, so move up the tree and 
-               // repeat.
-               node = node.parent;
+                // Case 2: Sibling has no red children.
+                // Recolor sibling, and move to parent.
+                sibling.color = RED;
+                node = parent;
+                parent = parent.parent;
               }
-           else
-             {       
-               if (sibling.right.color == BLACK)
-                 {
-                   // Case 3: Sibling has red left child.
-                   sibling.left.color = BLACK;
-                   sibling.color = RED;
+            else
+              {
+                if (sibling.right.color == BLACK)
+                  {
+                    // Case 3: Sibling has red left child.
+                    // Recolor sibling and left child, rotate sibling right.
+                    sibling.left.color = BLACK;
+                    sibling.color = RED;
                     rotateRight(sibling);
-                    sibling = node.parent.right;
-                 }               
-               
-               // Case 4: Sibling has red right child.
-               sibling.color = sibling.parent.color;
-               sibling.parent.color = BLACK;
-               sibling.right.color = BLACK;
-                rotateLeft(node.parent);
+                    sibling = parent.right;
+                  }
+                // Case 4: Sibling has red right child. Recolor sibling,
+                // right child, and parent, and rotate parent left.
+                sibling.color = parent.color;
+                parent.color = BLACK;
+                sibling.right.color = BLACK;
+                rotateLeft(parent);
                 node = root; // Finished.
-             }
-         }
-       else
-         {
-           // Symmetric "mirror" of left-side case.
-           Node sibling = node.parent.left;
-           if (sibling.color == RED)
-             {
+              }
+          }
+        else
+          {
+            // Symmetric "mirror" of left-side case.
+            Node sibling = parent.left;
+            // if (sibling == nil)
+            //   throw new InternalError();
+            if (sibling.color == RED)
+              {
+                // Case 1: Sibling is red.
+                // Recolor sibling and parent, and rotate parent right.
                 sibling.color = BLACK;
-                node.parent.color = RED;
-                rotateRight(node.parent);
-                sibling = node.parent.left;
-             }
+                parent.color = RED;
+                rotateRight(parent);
+                sibling = parent.left;
+              }
 
-           if (sibling.left.color == BLACK && sibling.right.color == BLACK)
+            if (sibling.right.color == BLACK && sibling.left.color == BLACK)
               {
-               sibling.color = RED;
-               node = node.parent;
+                // Case 2: Sibling has no red children.
+                // Recolor sibling, and move to parent.
+                sibling.color = RED;
+                node = parent;
+                parent = parent.parent;
               }
-           else
-             {       
-               if (sibling.left.color == BLACK)
-                 {
-                   sibling.right.color = BLACK;
-                   sibling.color = RED;
+            else
+              {
+                if (sibling.left.color == BLACK)
+                  {
+                    // Case 3: Sibling has red right child.
+                    // Recolor sibling and right child, rotate sibling left.
+                    sibling.right.color = BLACK;
+                    sibling.color = RED;
                     rotateLeft(sibling);
-                    sibling = node.parent.left;
-                 }               
-               
-               sibling.color = sibling.parent.color;
-               sibling.parent.color = BLACK;
-               sibling.left.color = BLACK;
-                rotateRight(node.parent);
-                node = root;
-             }
-         }
+                    sibling = parent.left;
+                  }
+                // Case 4: Sibling has red left child. Recolor sibling,
+                // left child, and parent, and rotate parent right.
+                sibling.color = parent.color;
+                parent.color = BLACK;
+                sibling.left.color = BLACK;
+                rotateRight(parent);
+                node = root; // Finished.
+              }
+          }
       }
     node.color = BLACK;
   }
 
-  public SortedMap subMap(Object fromKey, Object toKey)
+  /**
+   * Construct a perfectly balanced tree consisting of n "blank" nodes. This
+   * permits a tree to be generated from pre-sorted input in linear time.
+   *
+   * @param count the number of blank nodes, non-negative
+   */
+  private void fabricateTree(final int count)
   {
-    if (compare(fromKey, toKey) <= 0)
-      return new SubMap(fromKey, toKey);
-    else
-      throw new IllegalArgumentException("fromKey > toKey");
+    if (count == 0)
+      return;
+
+    // We color every row of nodes black, except for the overflow nodes.
+    // I believe that this is the optimal arrangement. We construct the tree
+    // in place by temporarily linking each node to the next node in the row,
+    // then updating those links to the children when working on the next row.
+
+    // Make the root node.
+    root = new Node(null, null, BLACK);
+    size = count;
+    Node row = root;
+    int rowsize;
+
+    // Fill each row that is completely full of nodes.
+    for (rowsize = 2; rowsize + rowsize < count; rowsize <<= 1)
+      {
+        Node parent = row;
+        Node last = null;
+        for (int i = 0; i < rowsize; i += 2)
+          {
+            Node left = new Node(null, null, BLACK);
+            Node right = new Node(null, null, BLACK);
+            left.parent = parent;
+            left.right = right;
+            right.parent = parent;
+            parent.left = left;
+            Node next = parent.right;
+            parent.right = right;
+            parent = next;
+            if (last != null)
+              last.right = left;
+            last = right;
+          }
+        row = row.left;
+      }
+
+    // Now do the partial final row in red.
+    int overflow = count - rowsize;
+    Node parent = row;
+    int i;
+    for (i = 0; i < overflow; i += 2)
+      {
+        Node left = new Node(null, null, RED);
+        Node right = new Node(null, null, RED);
+        left.parent = parent;
+        right.parent = parent;
+        parent.left = left;
+        Node next = parent.right;
+        parent.right = right;
+        parent = next;
+      }
+    // Add a lone left node if necessary.
+    if (i - overflow == 0)
+      {
+        Node left = new Node(null, null, RED);
+        left.parent = parent;
+        parent.left = left;
+        parent = parent.right;
+        left.parent.right = nil;
+      }
+    // Unlink the remaining nodes of the previous row.
+    while (parent != nil)
+      {
+        Node next = parent.right;
+        parent.right = nil;
+        parent = next;
+      }
   }
 
-  public SortedMap headMap(Object toKey)
+  /**
+   * Returns the first sorted node in the map, or nil if empty. Package
+   * visible for use by nested classes.
+   *
+   * @return the first node
+   */
+  final Node firstNode()
   {
-    return new SubMap(nil, toKey);
+    // Exploit fact that nil.left == nil.
+    Node node = root;
+    while (node.left != nil)
+      node = node.left;
+    return node;
   }
 
-  public SortedMap tailMap(Object fromKey)
+  /**
+   * Return the TreeMap.Node associated with key, or the nil node if no such
+   * node exists in the tree. Package visible for use by nested classes.
+   *
+   * @param key the key to search for
+   * @return the node where the key is found, or nil
+   */
+  final Node getNode(Object key)
   {
-    return new SubMap(fromKey, nil);
+    Node current = root;
+    while (current != nil)
+      {
+        int comparison = compare(key, current.key);
+        if (comparison > 0)
+          current = current.right;
+        else if (comparison < 0)
+          current = current.left;
+        else
+          return current;
+      }
+    return current;
+  }
+
+  /**
+   * Find the "highest" node which is &lt; key. If key is nil, return last
+   * node. Package visible for use by nested classes.
+   *
+   * @param key the upper bound, exclusive
+   * @return the previous node
+   */
+  final Node highestLessThan(Object key)
+  {
+    if (key == nil)
+      return lastNode();
+
+    Node last = nil;
+    Node current = root;
+    int comparison = 0;
+
+    while (current != nil)
+      {
+        last = current;
+        comparison = compare(key, current.key);
+        if (comparison > 0)
+          current = current.right;
+        else if (comparison < 0)
+          current = current.left;
+        else // Exact match.
+          return predecessor(last);
+      }
+    return comparison <= 0 ? predecessor(last) : last;
+  }
+
+  /**
+   * Maintain red-black balance after inserting a new node.
+   *
+   * @param n the newly inserted node
+   */
+  private void insertFixup(Node n)
+  {
+    // Only need to rebalance when parent is a RED node, and while at least
+    // 2 levels deep into the tree (ie: node has a grandparent). Remember
+    // that nil.color == BLACK.
+    while (n.parent.color == RED && n.parent.parent != nil)
+      {
+        if (n.parent == n.parent.parent.left)
+          {
+            Node uncle = n.parent.parent.right;
+            // Uncle may be nil, in which case it is BLACK.
+            if (uncle.color == RED)
+              {
+                // Case 1. Uncle is RED: Change colors of parent, uncle,
+                // and grandparent, and move n to grandparent.
+                n.parent.color = BLACK;
+                uncle.color = BLACK;
+                uncle.parent.color = RED;
+                n = uncle.parent;
+              }
+            else
+              {
+                if (n == n.parent.right)
+                  {
+                    // Case 2. Uncle is BLACK and x is right child.
+                    // Move n to parent, and rotate n left.
+                    n = n.parent;
+                    rotateLeft(n);
+                  }
+                // Case 3. Uncle is BLACK and x is left child.
+                // Recolor parent, grandparent, and rotate grandparent right.
+                n.parent.color = BLACK;
+                n.parent.parent.color = RED;
+                rotateRight(n.parent.parent);
+              }
+          }
+        else
+          {
+            // Mirror image of above code.
+            Node uncle = n.parent.parent.left;
+            // Uncle may be nil, in which case it is BLACK.
+            if (uncle.color == RED)
+              {
+                // Case 1. Uncle is RED: Change colors of parent, uncle,
+                // and grandparent, and move n to grandparent.
+                n.parent.color = BLACK;
+                uncle.color = BLACK;
+                uncle.parent.color = RED;
+                n = uncle.parent;
+              }
+            else
+              {
+                if (n == n.parent.left)
+                {
+                    // Case 2. Uncle is BLACK and x is left child.
+                    // Move n to parent, and rotate n right.
+                    n = n.parent;
+                    rotateRight(n);
+                  }
+                // Case 3. Uncle is BLACK and x is right child.
+                // Recolor parent, grandparent, and rotate grandparent left.
+                n.parent.color = BLACK;
+                n.parent.parent.color = RED;
+                rotateLeft(n.parent.parent);
+              }
+          }
+      }
+    root.color = BLACK;
   }
 
-  /** Returns a "collection view" (or "bag view") of this TreeMap's values. */
-  public Collection values()
+  /**
+   * Returns the last sorted node in the map, or nil if empty.
+   *
+   * @return the last node
+   */
+  private Node lastNode()
   {
-    // We don't bother overriding many of the optional methods, as doing so
-    // wouldn't provide any significant performance advantage.
-    return new AbstractCollection()
-    {
-      public int size()
-      {
-        return size;
-      }
-      
-      public Iterator iterator()
-      {
-        return new TreeIterator(TreeIterator.VALUES);
-      }
-      
-      public void clear()
-      {
-        TreeMap.this.clear();
-      }
-    };
+    // Exploit fact that nil.right == nil.
+    Node node = root;
+    while (node.right != nil)
+      node = node.right;
+    return node;
   }
 
-  // Find the "highest" node which is < key. If key is nil, return last node.
-  // Note that highestLessThan is exclusive (it won't return a key which is
-  // equal to "key"), while lowestGreaterThan is inclusive, in order to be 
-  // consistent with the semantics of subMap().
-  private Node highestLessThan(Object key)
+  /**
+   * Find the "lowest" node which is &gt;= key. If key is nil, return either
+   * nil or the first node, depending on the parameter first.
+   * Package visible for use by nested classes.
+   *
+   * @param key the lower bound, inclusive
+   * @param first true to return the first element instead of nil for nil key
+   * @return the next node
+   */
+  final Node lowestGreaterThan(Object key, boolean first)
   {
     if (key == nil)
-      return lastNode();
-  
+      return first ? firstNode() : nil;
+
     Node last = nil;
     Node current = root;
     int comparison = 0;
@@ -734,122 +1086,176 @@ public class TreeMap extends AbstractMap
       {
         last = current;
         comparison = compare(key, current.key);
-       if (comparison > 0)
-         current = current.right;
-       else if (comparison < 0)
-         current = current.left;
-       else /* Exact match. */
-         return predecessor(last);
+        if (comparison > 0)
+          current = current.right;
+        else if (comparison < 0)
+          current = current.left;
+        else
+          return current;
       }
-    if (comparison <= 0)
-      return predecessor(last);
-    else
-      return last;
+    return comparison > 0 ? successor(last) : last;
   }
 
-  // Find the "lowest" node which is >= key. If key is nil, return first node.
-  private Node lowestGreaterThan(Object key)
+  /**
+   * Return the node preceding the given one, or nil if there isn't one.
+   *
+   * @param node the current node, not nil
+   * @return the prior node in sorted order
+   */
+  private Node predecessor(Node node)
   {
-    if (key == nil)
-      return firstNode();
-
-    Node last = nil;
-    Node current = root;
-    int comparison = 0;
+    if (node.left != nil)
+      {
+        node = node.left;
+        while (node.right != nil)
+          node = node.right;
+        return node;
+      }
 
-    while (current != nil)
+    Node parent = node.parent;
+    // Exploit fact that nil.left == nil and node is non-nil.
+    while (node == parent.left)
       {
-        last = current;
-        comparison = compare(key, current.key);
-       if (comparison > 0)
-         current = current.right;
-       else if (comparison < 0)
-         current = current.left;
-       else
-         return current;
+        node = parent;
+        parent = node.parent;
       }
-    if (comparison > 0)
-      return successor(last);
-    else
-      return last;
-  }  
+    return parent;
+  }
 
-  private void writeObject(ObjectOutputStream out) throws IOException
+  /**
+   * Construct a tree from sorted keys in linear time. Package visible for
+   * use by TreeSet.
+   *
+   * @param s the stream to read from
+   * @param count the number of keys to read
+   * @param readValue true to read values, false to insert "" as the value
+   * @throws ClassNotFoundException if the underlying stream fails
+   * @throws IOException if the underlying stream fails
+   * @see #readObject(ObjectInputStream)
+   * @see TreeSet#readObject(ObjectInputStream)
+   */
+  final void putFromObjStream(ObjectInputStream s, int count,
+                              boolean readValues)
+    throws IOException, ClassNotFoundException
   {
-    out.defaultWriteObject();
-
+    fabricateTree(count);
     Node node = firstNode();
-    out.writeInt(size);
-    
-    while (node != nil)
+
+    while (--count >= 0)
       {
-        out.writeObject(node.key);
-       out.writeObject(node.value);
-       node = successor(node);
+        node.key = s.readObject();
+        node.value = readValues ? s.readObject() : "";
+        node = successor(node);
       }
   }
 
-  private void readObject(ObjectInputStream in)
-    throws IOException, ClassNotFoundException
+  /**
+   * Construct a tree from sorted keys in linear time, with values of "".
+   * Package visible for use by TreeSet.
+   *
+   * @param keys the iterator over the sorted keys
+   * @param count the number of nodes to insert
+   * @see TreeSet#TreeSet(SortedSet)
+   */
+  final void putKeysLinear(Iterator keys, int count)
   {
-    in.defaultReadObject();
-    int size = in.readInt();
-    putFromObjStream(in, size, true);
+    fabricateTree(count);
+    Node node = firstNode();
+
+    while (--count >= 0)
+      {
+        node.key = keys.next();
+        node.value = "";
+        node = successor(node);
+      }
   }
 
-  private int compare(Object o1, Object o2)
+  /**
+   * Deserializes this object from the given stream.
+   *
+   * @param s the stream to read from
+   * @throws ClassNotFoundException if the underlying stream fails
+   * @throws IOException if the underlying stream fails
+   * @serialData the <i>size</i> (int), followed by key (Object) and value
+   *             (Object) pairs in sorted order
+   */
+  private void readObject(ObjectInputStream s)
+    throws IOException, ClassNotFoundException
   {
-    if (comparator == null)
-      return ((Comparable) o1).compareTo(o2);
-    else
-      return comparator.compare(o1, o2);
+    s.defaultReadObject();
+    int size = s.readInt();
+    putFromObjStream(s, size, true);
   }
 
-  /* Return the node following Node, or nil if there isn't one. */
-  private Node successor(Node node)
+  /**
+   * Remove node from tree. This will increment modCount and decrement size.
+   * Node must exist in the tree. Package visible for use by nested classes.
+   *
+   * @param node the node to remove
+   */
+  final void removeNode(Node node)
   {
-    if (node.right != nil)
+    Node splice;
+    Node child;
+
+    modCount++;
+    size--;
+
+    // Find splice, the node at the position to actually remove from the tree.
+    if (node.left == nil)
       {
-        node = node.right;
-       while (node.left != nil)
-         node = node.left;
-       return node;
+        // Node to be deleted has 0 or 1 children.
+        splice = node;
+        child = node.right;
       }
-
-    Node parent = node.parent;
-    while (parent != nil && node == parent.right)
+    else if (node.right == nil)
       {
-       node = parent;
-       parent = parent.parent;
+        // Node to be deleted has 1 child.
+        splice = node;
+        child = node.left;
       }
-    return parent;
-  }
-
-  /* Return the node preceeding Node, or nil if there isn't one. */
-  private Node predecessor(Node node)
-  {
-    if (node.left != nil)
+    else
       {
-        node = node.left;
-       while (node.right != nil)
-         node = node.right;
-       return node;
+        // Node has 2 children. Splice is node's predecessor, and we swap
+        // its contents into node.
+        splice = node.left;
+        while (splice.right != nil)
+          splice = splice.right;
+        child = splice.left;
+        node.key = splice.key;
+        node.value = splice.value;
       }
-      
-    Node parent = node.parent;
-    while (parent != nil && node == parent.left)
+
+    // Unlink splice from the tree.
+    Node parent = splice.parent;
+    if (child != nil)
+      child.parent = parent;
+    if (parent == nil)
       {
-       node = parent;
-       parent = parent.parent;
+        // Special case for 0 or 1 node remaining.
+        root = child;
+        return;
       }
-    return parent;
+    if (splice == parent.left)
+      parent.left = child;
+    else
+      parent.right = child;
+
+    if (splice.color == BLACK)
+      deleteFixup(child, parent);
   }
 
-  /** Rotate node n to the left. */
+  /**
+   * Rotate node n to the left.
+   *
+   * @param node the node to rotate
+   */
   private void rotateLeft(Node node)
   {
     Node child = node.right;
-    
+    // if (node == nil || child == nil)
+    //   throw new InternalError();
+
     // Establish node.right link.
     node.right = child.left;
     if (child.left != nil)
@@ -860,331 +1266,146 @@ public class TreeMap extends AbstractMap
     if (node.parent != nil)
       {
         if (node == node.parent.left)
-         node.parent.left = child;
-       else
-         node.parent.right = child;
+          node.parent.left = child;
+        else
+          node.parent.right = child;
       }
     else
       root = child;
 
     // Link n and child.
     child.left = node;
-    if (node != nil)
-      node.parent = child;
+    node.parent = child;
   }
 
-  /** Rotate node n to the right. */
+  /**
+   * Rotate node n to the right.
+   *
+   * @param node the node to rotate
+   */
   private void rotateRight(Node node)
   {
     Node child = node.left;
-    
+    // if (node == nil || child == nil)
+    //   throw new InternalError();
+
     // Establish node.left link.
     node.left = child.right;
     if (child.right != nil)
       child.right.parent = node;
-      
+
     // Establish child->parent link.
     child.parent = node.parent;
     if (node.parent != nil)
       {
         if (node == node.parent.right)
-         node.parent.right = child;
-       else
-         node.parent.left = child;
+          node.parent.right = child;
+        else
+          node.parent.left = child;
       }
     else
       root = child;
-    
+
     // Link n and child.
     child.right = node;
-    if (node != nil)
-      node.parent = child;
-  }
-  
-  /* Construct a tree from sorted keys in linear time. This is used to
-     implement TreeSet's SortedSet constructor. */
-  void putKeysLinear(Iterator keys, int count)
-  {
-    fabricateTree(count);    
-    Node node = firstNode();
-    
-    for (int i = 0; i < count; i++)
-      {
-       node.key = keys.next();
-       node.value = Boolean.TRUE;
-       node = successor(node);
-      }
-  }
-  
-  /* As above, but load keys from an ObjectInputStream. Used by readObject()
-     methods. If "readValues" is set, entry values will also be read from the 
-     stream. If not, only keys will be read. */
-  void putFromObjStream(ObjectInputStream in, int count, boolean readValues) 
-    throws IOException, ClassNotFoundException
-  {
-    fabricateTree(count);    
-    Node node = firstNode();
-    
-    for (int i = 0; i < count; i++)
-      {
-       node.key = in.readObject();
-       if (readValues)
-         node.value = in.readObject();
-       else
-         node.value = Boolean.TRUE;      
-       node = successor(node);
-      }
-  }
-     
-  /* Construct a perfectly balanced tree consisting of n "blank" nodes. 
-     This permits a tree to be generated from pre-sorted input in linear 
-     time. */
-  private void fabricateTree(int count)
-  {
-    if (count == 0)
-      return;
-    // Calculate the (maximum) depth of the perfectly balanced tree.
-    double ddepth = (Math.log (count + 1) / Math.log (2));
-    int maxdepth = (int) Math.ceil (ddepth);
-    
-    // The number of nodes which can fit in a perfectly-balanced tree of 
-    // height "depth - 1".
-    int max = (int) Math.pow (2, maxdepth - 1) - 1;
-    
-    // Number of nodes which spill over into the deepest row of the tree.
-    int overflow = (int) count - max;
-    
-    size = count;
-    // Make the root node.
-    root = new Node(null, null);
-    root.parent = nil;
-    root.left = nil;
-    root.right = nil;
-    
-    Node row = root;
-    for (int depth = 2; depth <= maxdepth; depth++)  // each row
-      {        
-       // Number of nodes at this depth
-       int rowcap = (int) Math.pow (2, depth - 1);
-       Node parent = row;
-       Node last = null;
-       
-       // Actual number of nodes to create in this row
-       int rowsize;
-       if (depth == maxdepth)
-         rowsize = overflow;
-       else
-         rowsize = rowcap;
-       
-       // The bottom most row of nodes is coloured red, as is every second row 
-       // going up, except the root node (row 1). I'm not sure if this is the 
-       // optimal configuration for the tree, but it seems logical enough.
-       // We just need to honour the black-height and red-parent rules here.
-       boolean colorRowRed = (depth % 2 == maxdepth % 2);
-       
-       int i;
-       for (i = 1; i <= rowsize; i++)  // each node in row
-         {
-           Node node = new Node(null, null);
-           node.parent = parent;
-           if (i % 2 == 1)
-             parent.left = node;
-           else
-             {
-               Node nextparent = parent.right;
-               parent.right = node;
-               parent = nextparent;
-             }
-
-           // We use the "right" link to maintain a chain of nodes in 
-           // each row until the parent->child links are established.
-           if (last != null)
-             last.right = node;
-           last = node;
-           
-           if (colorRowRed)
-             node.color = RED;
-           
-           if (i == 1)
-             row = node;
-         }
-
-        // Set nil child pointers on leaf nodes.
-       if (depth == maxdepth)
-         {
-           // leaf nodes at maxdepth-1.
-           if (parent != null)
-             {
-               if (i % 2 == 0)
-                 {
-                   // Current "parent" has "left" set already.
-                   Node next = parent.right;
-                   parent.right = nil;
-                   parent = next;
-                 }                               
-               while (parent != null)
-                 {
-                   parent.left = nil;
-                   Node next = parent.right;
-                   parent.right = nil;
-                   parent = next;
-                 }
-             }
-           // leaf nodes at maxdepth.
-           Node node = row;
-           Node next;
-           while (node != null)
-             {
-               node.left = nil;
-               next = node.right;
-               node.right = nil;
-               node = next;
-             }
-         }
-      }
-  }
-  
-  private class VerifyResult
-  {
-    int count; // Total number of nodes.
-    int black; // Black height/depth.
-    int maxdepth; // Maximum depth of branch.
+    node.parent = child;
   }
 
-  /* Check that red-black properties are consistent for the tree. */
-  private void verifyTree()
-  {
-    if (root == nil)
-      {
-        System.err.println ("Verify: empty tree");
-       if (size != 0)
-         verifyError (this, "no root node but size=" + size);
-       return;
-      }
-    VerifyResult vr = verifySub (root);
-    if (vr.count != size)
-      {
-       verifyError (this, "Tree size not consistent with actual nodes counted. "
-                     + "counted " + vr.count + ", size=" + size);
-        System.exit(1);
-      }
-    System.err.println ("Verify: " + vr.count + " nodes, black height=" + vr.black
-                        + ", maxdepth=" + vr.maxdepth);
-  }
-  
-  /* Recursive call to check that rbtree rules hold. Returns total node count
-     and black height of the given branch. */
-  private VerifyResult verifySub(Node n)
+  /**
+   * Return the node following the given one, or nil if there isn't one.
+   * Package visible for use by nested classes.
+   *
+   * @param node the current node, not nil
+   * @return the next node in sorted order
+   */
+  final Node successor(Node node)
   {
-    VerifyResult vr1 = null;
-    VerifyResult vr2 = null;
-    
-    if (n.left == nil && n.right == nil)
-      {
-        // leaf node
-       VerifyResult r = new VerifyResult();
-       r.black = (n.color == BLACK ? 1 : 0);
-       r.count = 1;
-       r.maxdepth = 1;
-       return r;
-      }
-    
-    if (n.left != nil)
+    if (node.right != nil)
       {
-        if (n.left.parent != n)
-         verifyError(n.left, "Node's parent link does not point to " + n);
-       
-       if (n.color == RED && n.left.color == RED)
-         verifyError(n, "Red node has red left child");
-       
-       vr1 = verifySub (n.left);
-       if (n.right == nil)
-         {
-           if (n.color == BLACK)
-             vr1.black++;
-           vr1.count++;
-           vr1.maxdepth++;
-           return vr1;
-         }
+        node = node.right;
+        while (node.left != nil)
+          node = node.left;
+        return node;
       }
 
-    if (n.right != nil)
+    Node parent = node.parent;
+    // Exploit fact that nil.right == nil and node is non-nil.
+    while (node == parent.right)
       {
-        if (n.right.parent != n)
-         verifyError(n.right, "Node's parent link does not point to " + n);
-
-       if (n.color == RED && n.right.color == RED)
-         verifyError(n, "Red node has red right child");
-
-       vr2 = verifySub (n.right);
-       if (n.left == nil)
-         {
-           if (n.color == BLACK)
-             vr2.black++;
-           vr2.count++;
-           vr2.maxdepth++;
-           return vr2;
-         }
+        node = parent;
+        parent = parent.parent;
       }
-    
-    if (vr1.black != vr2.black)
-      verifyError (n, "Black heights: " + vr1.black + "," + vr2.black + " don't match.");
-    vr1.count += vr2.count + 1;
-    vr1.maxdepth = Math.max(vr1.maxdepth, vr2.maxdepth) + 1;
-    if (n.color == BLACK)
-      vr1.black++;
-    return vr1;
+    return parent;
   }
-  
-  private void verifyError (Object obj, String msg)
+
+  /**
+   * Serializes this object to the given stream.
+   *
+   * @param s the stream to write to
+   * @throws IOException if the underlying stream fails
+   * @serialData the <i>size</i> (int), followed by key (Object) and value
+   *             (Object) pairs in sorted order
+   */
+  private void writeObject(ObjectOutputStream s) throws IOException
   {
-    System.err.print ("Verify error: ");
-    try
-      {
-        System.err.print (obj);
-      }
-    catch (Exception x)
+    s.defaultWriteObject();
+
+    Node node = firstNode();
+    s.writeInt(size);
+    while (node != nil)
       {
-        System.err.print ("(error printing obj): " + x);
+        s.writeObject(node.key);
+        s.writeObject(node.value);
+        node = successor(node);
       }
-    System.err.println();
-    System.err.println (msg);
-    Thread.dumpStack();
-    System.exit(1);
   }
 
   /**
-   * Iterate over HashMap's entries.
-   * This implementation is parameterized to give a sequential view of
-   * keys, values, or entries.
-   */   
-  class TreeIterator implements Iterator
+   * Iterate over HashMap's entries. This implementation is parameterized
+   * to give a sequential view of keys, values, or entries.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private final class TreeIterator implements Iterator
   {
-    static final int ENTRIES = 0,
-                     KEYS = 1,
-                     VALUES = 2;  
-  
-    // the type of this Iterator: KEYS, VALUES, or ENTRIES.
-    int type;
-    // the number of modifications to the backing Map that we know about.
-    int knownMod = TreeMap.this.modCount;
-    // The last Entry returned by a next() call.
-    Node last;
-    // The next entry that should be returned by next().
-    Node next;
-    // The last node visible to this iterator. This is used when iterating
-    // on a SubMap.
-    Node max;
-
-    /* Create Iterator with the supplied type: KEYS, VALUES, or ENTRIES */
+    /**
+     * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
+     * or {@link #ENTRIES}.
+     */
+    private final int type;
+    /** The number of modifications to the backing Map that we know about. */
+    private int knownMod = modCount;
+    /** The last Entry returned by a next() call. */
+    private Node last;
+    /** The next entry that should be returned by next(). */
+    private Node next;
+    /**
+     * The last node visible to this iterator. This is used when iterating
+     * on a SubMap.
+     */
+    private final Node max;
+
+    /**
+     * Construct a new TreeIterator with the supplied type.
+     * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+     */
     TreeIterator(int type)
     {
+      // FIXME gcj cannot handle this. Bug java/4695
+      // this(type, firstNode(), nil);
       this.type = type;
       this.next = firstNode();
+      this.max = nil;
     }
-    
-    /* Construct an interator for a SubMap. Iteration will begin at node
-       "first", and stop when "max" is reached. */    
+
+    /**
+     * Construct a new TreeIterator with the supplied type. Iteration will
+     * be from "first" (inclusive) to "max" (exclusive).
+     *
+     * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+     * @param first where to start iteration, nil for empty iterator
+     * @param max the cutoff for iteration, nil for all remaining nodes
+     */
     TreeIterator(int type, Node first, Node max)
     {
       this.type = type;
@@ -1192,263 +1413,351 @@ public class TreeMap extends AbstractMap
       this.max = max;
     }
 
+    /**
+     * Returns true if the Iterator has more elements.
+     * @return true if there are more elements
+     * @throws ConcurrentModificationException if the TreeMap was modified
+     */
     public boolean hasNext()
     {
-      if (knownMod != TreeMap.this.modCount)
-       throw new ConcurrentModificationException();
-      return (next != nil);
+      if (knownMod != modCount)
+        throw new ConcurrentModificationException();
+      return next != max;
     }
 
+    /**
+     * Returns the next element in the Iterator's sequential view.
+     * @return the next element
+     * @throws ConcurrentModificationException if the TreeMap was modified
+     * @throws NoSuchElementException if there is none
+     */
     public Object next()
     {
-      if (next == nil)
-       throw new NoSuchElementException();
-      if (knownMod != TreeMap.this.modCount)
-       throw new ConcurrentModificationException();
-      Node n = next;
-
-      // Check limit in case we are iterating through a submap.
-      if (n != max)
-       next = successor(n);
-      else
-        next = nil;
-      
-      last = n;
-      
+      if (knownMod != modCount)
+        throw new ConcurrentModificationException();
+      if (next == max)
+        throw new NoSuchElementException();
+      last = next;
+      next = successor(last);
+
       if (type == VALUES)
-        return n.value;
+        return last.value;
       else if (type == KEYS)
-        return n.key;
-      return n;
+        return last.key;
+      return last;
     }
 
+    /**
+     * Removes from the backing TreeMap the last element which was fetched
+     * with the <code>next()</code> method.
+     * @throws ConcurrentModificationException if the TreeMap was modified
+     * @throws IllegalStateException if called when there is no last element
+     */
     public void remove()
     {
+      if (knownMod != modCount)
+        throw new ConcurrentModificationException();
       if (last == null)
-       throw new IllegalStateException();
-      if (knownMod != TreeMap.this.modCount)
-       throw new ConcurrentModificationException();
-/*
-      Object key = null;
-      if (next != nil)
-        key = next.key;
-*/
-      TreeMap.this.removeNode(last);
-      knownMod++;
-/*
-      if (key != null)
-        next = getNode(key);
-*/     
+        throw new IllegalStateException();
+
+      removeNode(last);
       last = null;
+      knownMod++;
     }
-  }
+  } // class TreeIterator
 
-  class SubMap extends AbstractMap implements SortedMap
+  /**
+   * Implementation of {@link #subMap(Object, Object)} and other map
+   * ranges. This class provides a view of a portion of the original backing
+   * map, and throws {@link IllegalArgumentException} for attempts to
+   * access beyond that range.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private final class SubMap extends AbstractMap implements SortedMap
   {
-    Object minKey;
-    Object maxKey;
-
-    /* Create a SubMap representing the elements between minKey and maxKey
-       (inclusive). If minKey is nil, SubMap has no lower bound (headMap).
-       If maxKey is nil, the SubMap has no upper bound (tailMap). */
+    /**
+     * The lower range of this view, inclusive, or nil for unbounded.
+     * Package visible for use by nested classes.
+     */
+    final Object minKey;
+
+    /**
+     * The upper range of this view, exclusive, or nil for unbounded.
+     * Package visible for use by nested classes.
+     */
+    final Object maxKey;
+
+    /**
+     * The cache for {@link #entrySet()}.
+     */
+    private Set entries;
+
+    /**
+     * Create a SubMap representing the elements between minKey (inclusive)
+     * and maxKey (exclusive). If minKey is nil, SubMap has no lower bound
+     * (headMap). If maxKey is nil, the SubMap has no upper bound (tailMap).
+     *
+     * @param minKey the lower bound
+     * @param maxKey the upper bound
+     * @throws IllegalArgumentException if minKey &gt; maxKey
+     */
     SubMap(Object minKey, Object maxKey)
     {
+      if (minKey != nil && maxKey != nil && compare(minKey, maxKey) > 0)
+        throw new IllegalArgumentException("fromKey > toKey");
       this.minKey = minKey;
       this.maxKey = maxKey;
     }
 
+    /**
+     * Check if "key" is in within the range bounds for this SubMap. The
+     * lower ("from") SubMap range is inclusive, and the upper ("to") bound
+     * is exclusive. Package visible for use by nested classes.
+     *
+     * @param key the key to check
+     * @return true if the key is in range
+     */
+    final boolean keyInRange(Object key)
+    {
+      return ((minKey == nil || compare(key, minKey) >= 0)
+              && (maxKey == nil || compare(key, maxKey) < 0));
+    }
+
     public void clear()
     {
-      Node current;
-      Node next = lowestGreaterThan(minKey);
-      Node max = highestLessThan(maxKey);
-      
-      if (compare(next.key, max.key) > 0)
-        // Nothing to delete.
-       return;
-        
-      do
+      Node next = lowestGreaterThan(minKey, true);
+      Node max = lowestGreaterThan(maxKey, false);
+      while (next != max)
         {
-         current = next;
-         next = successor(current);
-         remove(current);
-       }
-      while (current != max);
+          Node current = next;
+          next = successor(current);
+          removeNode(current);
+        }
     }
-    
-    /* Check if "key" is in within the range bounds for this SubMap. 
-       The lower ("from") SubMap range is inclusive, and the upper (to) bound
-       is exclusive. */
-    private boolean keyInRange(Object key)
+
+    public Comparator comparator()
     {
-      return ((minKey == nil || compare(key, minKey) >= 0)
-             && (maxKey == nil || compare(key, maxKey) < 0));
+      return comparator;
     }
 
     public boolean containsKey(Object key)
     {
-      return (keyInRange(key) && TreeMap.this.containsKey(key));
+      return keyInRange(key) && TreeMap.this.containsKey(key);
     }
 
     public boolean containsValue(Object value)
     {
-      Node node = lowestGreaterThan(minKey);
-      Node max = highestLessThan(maxKey);
-      Object currentVal;
-
-      if (node == nil || max == nil || compare(node.key, max.key) > 0)
-        // Nothing to search.
-       return false;
-
-      while (true)
-       {
-         currentVal = node.getValue();
-          if (value == null ? currentVal == null : value.equals (currentVal))
-           return true;
-         if (node == max)
-           return false;
-         node = successor(node);
-       }
+      Node node = lowestGreaterThan(minKey, true);
+      Node max = lowestGreaterThan(maxKey, false);
+      while (node != max)
+        {
+          if (equals(value, node.getValue()))
+            return true;
+          node = successor(node);
+        }
+      return false;
     }
 
-    public Object get(Object key)
+    public Set entrySet()
     {
-      if (keyInRange(key))
-       return TreeMap.this.get(key);
-      return null;
+      if (entries == null)
+        // Create an AbstractSet with custom implementations of those methods
+        // that can be overriden easily and efficiently.
+        entries = new AbstractSet()
+        {
+          public int size()
+          {
+            return SubMap.this.size();
+          }
+
+          public Iterator iterator()
+          {
+            Node first = lowestGreaterThan(minKey, true);
+            Node max = lowestGreaterThan(maxKey, false);
+            return new TreeIterator(ENTRIES, first, max);
+          }
+
+          public void clear()
+          {
+            SubMap.this.clear();
+          }
+
+          public boolean contains(Object o)
+          {
+            if (! (o instanceof Map.Entry))
+              return false;
+            Map.Entry me = (Map.Entry) o;
+            Object key = me.getKey();
+            if (! keyInRange(key))
+              return false;
+            Node n = getNode(key);
+            return n != nil && AbstractSet.equals(me.getValue(), n.value);
+          }
+
+          public boolean remove(Object o)
+          {
+            if (! (o instanceof Map.Entry))
+              return false;
+            Map.Entry me = (Map.Entry) o;
+            Object key = me.getKey();
+            if (! keyInRange(key))
+              return false;
+            Node n = getNode(key);
+            if (n != nil && AbstractSet.equals(me.getValue(), n.value))
+              {
+                removeNode(n);
+                return true;
+              }
+            return false;
+          }
+        };
+      return entries;
     }
 
-    public Object put(Object key, Object value)
+    public Object firstKey()
     {
-      if (keyInRange(key))
-       return TreeMap.this.put(key, value);
-      else
-       throw new IllegalArgumentException("Key outside range");
+      Node node = lowestGreaterThan(minKey, true);
+      if (node == nil || ! keyInRange(node.key))
+        throw new NoSuchElementException();
+      return node.key;
     }
 
-    public Object remove(Object key)
+    public Object get(Object key)
     {
       if (keyInRange(key))
-       return TreeMap.this.remove(key);
-      else
-        return null;
+        return TreeMap.this.get(key);
+      return null;
     }
 
-    public int size()
+    public SortedMap headMap(Object toKey)
     {
-      Node node = lowestGreaterThan(minKey);
-      Node max = highestLessThan(maxKey);
-
-      if (node == nil || max == nil || compare(node.key, max.key) > 0)
-       return 0;  // Empty.
+      if (! keyInRange(toKey))
+        throw new IllegalArgumentException("key outside range");
+      return new SubMap(minKey, toKey);
+    }
 
-      int count = 1;
-      while (node != max)
+    public Set keySet()
+    {
+      if (this.keys == null)
+        // Create an AbstractSet with custom implementations of those methods
+        // that can be overriden easily and efficiently.
+        this.keys = new AbstractSet()
         {
-         count++;
-         node = successor(node);
-       }
+          public int size()
+          {
+            return SubMap.this.size();
+          }
 
-      return count;
+          public Iterator iterator()
+          {
+            Node first = lowestGreaterThan(minKey, true);
+            Node max = lowestGreaterThan(maxKey, false);
+            return new TreeIterator(KEYS, first, max);
+          }
+
+          public void clear()
+          {
+            SubMap.this.clear();
+          }
+
+          public boolean contains(Object o)
+          {
+            if (! keyInRange(o))
+              return false;
+            return getNode(o) != nil;
+          }
+
+          public boolean remove(Object o)
+          {
+            if (! keyInRange(o))
+              return false;
+            Node n = getNode(o);
+            if (n != nil)
+              {
+                removeNode(n);
+                return true;
+              }
+            return false;
+          }
+        };
+      return this.keys;
     }
 
-    public Set entrySet()
+    public Object lastKey()
     {
-      // Create an AbstractSet with custom implementations of those methods that 
-      // can be overriden easily and efficiently.
-      return new AbstractSet()
-      {
-       public int size()
-       {
-          return SubMap.this.size();
-       }
-
-       public Iterator iterator()
-       {
-         Node first = lowestGreaterThan(minKey);
-         Node max = highestLessThan(maxKey);
-          return new TreeIterator(TreeIterator.ENTRIES, first, max);
-       }
-
-       public void clear()
-       {
-          this.clear();
-       }
-
-       public boolean contains(Object o)
-       {
-          if (!(o instanceof Map.Entry))
-           return false;
-         Map.Entry me = (Map.Entry) o;
-         Object key = me.getKey();
-         if (!keyInRange(key))
-           return false;
-         Node n = getNode(key);
-         return (n != nil && me.getValue().equals(n.value));
-       }
-
-       public boolean remove(Object o)
-       {
-          if (!(o instanceof Map.Entry))
-           return false;
-         Map.Entry me = (Map.Entry) o;
-         Object key = me.getKey();
-         if (!keyInRange(key))
-           return false;
-         Node n = getNode(key);
-         if (n != nil && me.getValue().equals(n.value))
-           {
-             removeNode(n);
-             return true;
-           }
-         return false;
-       }
-      };    
+      Node node = highestLessThan(maxKey);
+      if (node == nil || ! keyInRange(node.key))
+        throw new NoSuchElementException();
+      return node.key;
     }
 
-    public Comparator comparator()
+    public Object put(Object key, Object value)
     {
-      return comparator;
+      if (! keyInRange(key))
+        throw new IllegalArgumentException("Key outside range");
+      return TreeMap.this.put(key, value);
     }
 
-    public Object firstKey()
+    public Object remove(Object key)
     {
-      Node node = lowestGreaterThan(minKey);
-      if (node == nil || !keyInRange(node.key))
-        throw new NoSuchElementException ("empty");
-      return node.key;
+      if (keyInRange(key))
+        return TreeMap.this.remove(key);
+      return null;
     }
 
-    public Object lastKey()
+    public int size()
     {
-      Node node = highestLessThan(maxKey);
-      if (node == nil || !keyInRange(node.key))
-        throw new NoSuchElementException ("empty");
-      return node.key;
+      Node node = lowestGreaterThan(minKey, true);
+      Node max = lowestGreaterThan(maxKey, false);
+      int count = 0;
+      while (node != max)
+        {
+          count++;
+          node = successor(node);
+        }
+      return count;
     }
 
     public SortedMap subMap(Object fromKey, Object toKey)
     {
-      if (!keyInRange(fromKey) || !keyInRange(toKey))
+      if (! keyInRange(fromKey) || ! keyInRange(toKey))
         throw new IllegalArgumentException("key outside range");
-
-      return TreeMap.this.subMap(fromKey, toKey);
+      return new SubMap(fromKey, toKey);
     }
 
-    public SortedMap headMap(Object toKey)
+    public SortedMap tailMap(Object fromKey)
     {
-      if (!keyInRange(toKey))
+      if (! keyInRange(fromKey))
         throw new IllegalArgumentException("key outside range");
-
-      return TreeMap.this.subMap(minKey, toKey);
+      return new SubMap(fromKey, maxKey);
     }
 
-    public SortedMap tailMap(Object fromKey)
+    public Collection values()
     {
-      if (!keyInRange(fromKey))
-        throw new IllegalArgumentException("key outside range");
+      if (this.values == null)
+        // Create an AbstractCollection with custom implementations of those
+        // methods that can be overriden easily and efficiently.
+        this.values = new AbstractCollection()
+        {
+          public int size()
+          {
+            return SubMap.this.size();
+          }
 
-      return TreeMap.this.subMap(fromKey, maxKey);
+          public Iterator iterator()
+          {
+            Node first = lowestGreaterThan(minKey, true);
+            Node max = lowestGreaterThan(maxKey, false);
+            return new TreeIterator(VALUES, first, max);
+          }
+
+          public void clear()
+          {
+            SubMap.this.clear();
+          }
+        };
+      return this.keys;
     }
-  }
-}
+  } // class SubMap  
+} // class TreeMap
index ba852131a13b1dc728863a8495c61c9e74798a12..3d2ef3d24d190d3847f035f86467d91460bebcdb 100644 (file)
@@ -1,4 +1,4 @@
-/* TreeSet.java -- a class providing a TreeMap-backet SortedSet
+/* TreeSet.java -- a class providing a TreeMap-backed SortedSet
    Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
@@ -33,54 +33,91 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 /**
- * This class provides a TreeMap-backed implementation of the 
- * SortedSet interface.
+ * This class provides a TreeMap-backed implementation of the SortedSet
+ * interface. The elements will be sorted according to their <i>natural
+ * order</i>, or according to the provided <code>Comparator</code>.<p>
  *
- * Each element in the Set is a key in the backing TreeMap; each key
- * maps to a static token, denoting that the key does, in fact, exist.
+ * Most operations are O(log n), but there is so much overhead that this
+ * makes small sets expensive. Note that the ordering must be <i>consistent
+ * with equals</i> to correctly implement the Set interface. If this
+ * condition is violated, the set is still well-behaved, but you may have
+ * suprising results when comparing it to other sets.<p>
  *
- * Most operations are O(log n).
+ * This implementation is not synchronized. If you need to share this between
+ * multiple threads, do something like:<br>
+ * <code>SortedSet s
+ *       = Collections.synchronizedSortedSet(new TreeSet(...));</code><p>
  *
- * TreeSet is a part of the JDK1.2 Collections API.
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * <code>ConcurrentModificationException</code> rather than exhibit
+ * non-deterministic behavior.
  *
- * @author      Jon Zeppieri
+ * @author Jon Zeppieri
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see Set
+ * @see HashSet
+ * @see LinkedHashSet
+ * @see Comparable
+ * @see Comparator
+ * @see Collections#synchronizedSortedSet(SortedSet)
+ * @see TreeMap
+ * @since 1.2
+ * @status updated to 1.4
  */
-
 public class TreeSet extends AbstractSet
   implements SortedSet, Cloneable, Serializable
 {
-  /** The TreeMap which backs this Set */
-  transient SortedMap map;
+  /**
+   * Compatible with JDK 1.2.
+   */
+  private static final long serialVersionUID = -2479143000061671589L;
 
-  static final long serialVersionUID = -2479143000061671589L;
+  /**
+   * The SortedMap which backs this Set.
+   */
+  // Not final because of readObject. This will always be one of TreeMap or
+  // TreeMap.SubMap, which both extend AbstractMap.
+  private transient SortedMap map;
 
   /**
-   * Construct a new TreeSet whose backing TreeMap using the "natural" 
-   * ordering of keys.
+   * Construct a new TreeSet whose backing TreeMap using the "natural"
+   * ordering of keys. Elements that are not mutually comparable will cause
+   * ClassCastExceptions down the road.
+   *
+   * @see Comparable
    */
   public TreeSet()
   {
     map = new TreeMap();
   }
 
-  /** 
-   * Construct a new TreeSet whose backing TreeMap uses the supplied 
-   * Comparator.
+  /**
+   * Construct a new TreeSet whose backing TreeMap uses the supplied
+   * Comparator. Elements that are not mutually comparable will cause
+   * ClassCastExceptions down the road.
    *
-   * @param     oComparator      the Comparator this Set will use
+   * @param comparator the Comparator this Set will use
    */
   public TreeSet(Comparator comparator)
   {
     map = new TreeMap(comparator);
   }
 
-  /** 
+  /**
    * Construct a new TreeSet whose backing TreeMap uses the "natural"
    * orering of the keys and which contains all of the elements in the
-   * supplied Collection.
+   * supplied Collection. This runs in n*log(n) time.
    *
-   * @param     oCollection      the new Set will be initialized with all
-   *                             of the elements in this Collection
+   * @param collection the new Set will be initialized with all
+   *        of the elements in this Collection
+   * @throws ClassCastException if the elements of the collection are not
+   *         comparable
+   * @throws NullPointerException if the collection is null
+   * @see Comparable
    */
   public TreeSet(Collection collection)
   {
@@ -93,54 +130,57 @@ public class TreeSet extends AbstractSet
    * SortedSet and containing all of the elements in the supplied SortedSet.
    * This constructor runs in linear time.
    *
-   * @param     sortedSet       the new TreeSet will use this SortedSet's
-   *                            comparator and will initialize itself
-   *                            with all of the elements in this SortedSet
+   * @param sortedSet the new TreeSet will use this SortedSet's comparator
+   *        and will initialize itself with all its elements
+   * @throws NullPointerException if sortedSet is null
    */
   public TreeSet(SortedSet sortedSet)
   {
-    TreeMap map = new TreeMap(sortedSet.comparator());
-    int i = 0;
+    map = new TreeMap(sortedSet.comparator());
     Iterator itr = sortedSet.iterator();
-    map.putKeysLinear(itr, sortedSet.size());
-    this.map = map;
+    ((TreeMap) map).putKeysLinear(itr, sortedSet.size());
   }
-  
-  /* This private constructor is used to implement the subSet() calls around
-    a backing TreeMap.SubMap. */
-  TreeSet(SortedMap backingMap)
+
+  /**
+   * This private constructor is used to implement the subSet() calls around
+   * a backing TreeMap.SubMap.
+   *
+   * @param backingMap the submap
+   */
+  private TreeSet(SortedMap backingMap)
   {
     map = backingMap;
   }
 
-  /** 
+  /**
    * Adds the spplied Object to the Set if it is not already in the Set;
-   * returns true if the element is added, false otherwise
+   * returns true if the element is added, false otherwise.
    *
-   * @param       obj       the Object to be added to this Set
+   * @param obj the Object to be added to this Set
+   * @throws ClassCastException if the element cannot be compared with objects
+   *         already in the set
    */
   public boolean add(Object obj)
   {
-    return (map.put(obj, Boolean.TRUE) == null);
+    return map.put(obj, "") == null;
   }
 
   /**
    * Adds all of the elements in the supplied Collection to this TreeSet.
    *
-   * @param        c         All of the elements in this Collection
-   *                         will be added to the Set.
-   *
-   * @return       true if the Set is altered, false otherwise
+   * @param c The collection to add
+   * @return true if the Set is altered, false otherwise
+   * @throws NullPointerException if c is null
+   * @throws ClassCastException if an element in c cannot be compared with
+   *         objects already in the set
    */
   public boolean addAll(Collection c)
   {
     boolean result = false;
-    int size = c.size();
+    int pos = c.size();
     Iterator itr = c.iterator();
-
-    for (int i = 0; i < size; i++)
-      result |= (map.put(itr.next(), Boolean.TRUE) == null);
-
+    while (--pos >= 0)
+      result |= (map.put(itr.next(), "") == null);
     return result;
   }
 
@@ -152,137 +192,214 @@ public class TreeSet extends AbstractSet
     map.clear();
   }
 
-  /** Returns a shallow copy of this Set. */
+  /**
+   * Returns a shallow copy of this Set. The elements are not cloned.
+   *
+   * @return the cloned set
+   */
   public Object clone()
   {
     TreeSet copy = null;
     try
       {
         copy = (TreeSet) super.clone();
+        // Map may be either TreeMap or TreeMap.SubMap, hence the ugly casts.
+        copy.map = (SortedMap) ((AbstractMap) map).clone();
       }
     catch (CloneNotSupportedException x)
-      {      
+      {
+        // Impossible result.
       }
-    copy.map = (SortedMap) ((TreeMap) map).clone();
     return copy;
   }
 
-  /** Returns this Set's comparator */
+  /**
+   * Returns this Set's comparator.
+   *
+   * @return the comparator, or null if the set uses natural ordering
+   */
   public Comparator comparator()
   {
     return map.comparator();
   }
 
-  /** 
-   * Returns true if this Set contains the supplied Object, 
-   * false otherwise 
+  /**
+   * Returns true if this Set contains the supplied Object, false otherwise.
    *
-   * @param       oObject        the Object whose existence in the Set is
-   *                             being tested
+   * @param obj the Object to check for
+   * @return true if it is in the set
+   * @throws ClassCastException if obj cannot be compared with objects
+   *         already in the set
    */
   public boolean contains(Object obj)
   {
     return map.containsKey(obj);
   }
 
-  /** Returns true if this Set has size 0, false otherwise */
-  public boolean isEmpty()
+  /**
+   * Returns the first (by order) element in this Set.
+   *
+   * @return the first element
+   * @throws NoSuchElementException if the set is empty
+   */
+  public Object first()
   {
-    return map.isEmpty();
+    return map.firstKey();
   }
 
-  /** Returns the number of elements in this Set */
-  public int size()
+  /**
+   * Returns a view of this Set including all elements less than
+   * <code>to</code>. The returned set is backed by the original, so changes
+   * in one appear in the other. The subset will throw an
+   * {@link IllegalArgumentException} for any attempt to access or add an
+   * element beyond the specified cutoff. The returned set does not include
+   * the endpoint; if you want inclusion, pass the successor element.
+   *
+   * @param to the (exclusive) cutoff point
+   * @return a view of the set less than the cutoff
+   * @throws ClassCastException if <code>to</code> is not compatible with
+   *         the comparator (or is not Comparable, for natural ordering)
+   * @throws NullPointerException if to is null, but the comparator does not
+   *         tolerate null elements
+   */
+  public SortedSet headSet(Object to)
   {
-    return map.size();
+    return new TreeSet(map.headMap(to));
   }
 
-  /** 
-   * If the supplied Object is in this Set, it is removed, and true is
-   * returned; otherwise, false is returned.
+  /**
+   * Returns true if this Set has size 0, false otherwise.
    *
-   * @param         obj        the Object we are attempting to remove
-   *                           from this Set
+   * @return true if the set is empty
    */
-  public boolean remove(Object obj)
+  public boolean isEmpty()
   {
-    return (map.remove(obj) != null);
+    return map.isEmpty();
   }
 
-  /** Returns the first (by order) element in this Set */
-  public Object first()
+  /**
+   * Returns in Iterator over the elements in this TreeSet, which traverses
+   * in ascending order.
+   *
+   * @return an iterator
+   */
+  public Iterator iterator()
   {
-    return map.firstKey();
+    return map.keySet().iterator();
   }
 
-  /** Returns the last (by order) element in this Set */
+  /**
+   * Returns the last (by order) element in this Set.
+   *
+   * @return the last element
+   * @throws NoSuchElementException if the set is empty
+   */
   public Object last()
   {
     return map.lastKey();
   }
 
   /**
-   * Returns a view of this Set including all elements in the interval
-   * [oFromElement, oToElement).
+   * If the supplied Object is in this Set, it is removed, and true is
+   * returned; otherwise, false is returned.
    *
-   * @param       from  the resultant view will contain all
-   *                    elements greater than or equal to this element
-   * @param       to    the resultant view will contain all
-   *                    elements less than this element
+   * @param obj the Object to remove from this Set
+   * @return true if the set was modified
+   * @throws ClassCastException if obj cannot be compared to set elements
    */
-  public SortedSet subSet(Object from, Object to)
+  public boolean remove(Object obj)
   {
-    return new TreeSet(map.subMap(from, to));
+    return map.remove(obj) != null;
   }
 
   /**
-   * Returns a view of this Set including all elements less than oToElement
+   * Returns the number of elements in this Set
    *
-   * @param       toElement    the resultant view will contain all
-   *                            elements less than this element
+   * @return the set size
    */
-  public SortedSet headSet(Object to)
+  public int size()
   {
-    return new TreeSet(map.headMap(to));
+    return map.size();
   }
 
   /**
-   * Returns a view of this Set including all elements greater than or
-   * equal to oFromElement.
+   * Returns a view of this Set including all elements greater or equal to
+   * <code>from</code> and less than <code>to</code> (a half-open interval).
+   * The returned set is backed by the original, so changes in one appear in
+   * the other. The subset will throw an {@link IllegalArgumentException}
+   * for any attempt to access or add an element beyond the specified cutoffs.
+   * The returned set includes the low endpoint but not the high; if you want
+   * to reverse this behavior on either end, pass in the successor element.
    *
-   * @param       from  the resultant view will contain all
-   *              elements greater than or equal to this element
+   * @param from the (inclusive) low cutoff point
+   * @param to the (exclusive) high cutoff point
+   * @return a view of the set between the cutoffs
+   * @throws ClassCastException if either cutoff is not compatible with
+   *         the comparator (or is not Comparable, for natural ordering)
+   * @throws NullPointerException if from or to is null, but the comparator
+   *         does not tolerate null elements
+   * @throws IllegalArgumentException if from is greater than to
    */
-  public SortedSet tailSet(Object from)
+  public SortedSet subSet(Object from, Object to)
   {
-    return new TreeSet(map.tailMap(from));
+    return new TreeSet(map.subMap(from, to));
   }
 
-  /** Returns in Iterator over the elements in this TreeSet */
-  public Iterator iterator()
+  /**
+   * Returns a view of this Set including all elements greater or equal to
+   * <code>from</code>. The returned set is backed by the original, so
+   * changes in one appear in the other. The subset will throw an
+   * {@link IllegalArgumentException} for any attempt to access or add an
+   * element beyond the specified cutoff. The returned set includes the
+   * endpoint; if you want to exclude it, pass in the successor element.
+   *
+   * @param from the (inclusive) low cutoff point
+   * @return a view of the set above the cutoff
+   * @throws ClassCastException if <code>from</code> is not compatible with
+   *         the comparator (or is not Comparable, for natural ordering)
+   * @throws NullPointerException if from is null, but the comparator
+   *         does not tolerate null elements
+   */
+  public SortedSet tailSet(Object from)
   {
-    return map.keySet().iterator();
+    return new TreeSet(map.tailMap(from));
   }
 
-  private void writeObject(ObjectOutputStream out) throws IOException
+  /**
+   * Serializes this object to the given stream.
+   *
+   * @param s the stream to write to
+   * @throws IOException if the underlying stream fails
+   * @serialData the <i>comparator</i> (Object), followed by the set size
+   *             (int), the the elements in sorted order (Object)
+   */
+  private void writeObject(ObjectOutputStream s) throws IOException
   {
+    s.defaultWriteObject();
     Iterator itr = map.keySet().iterator();
-    int size = map.size();
-
-    out.writeObject(map.comparator());
-    out.writeInt(size);
-
-    for (int i = 0; i < size; i++)
-      out.writeObject(itr.next());
+    int pos = map.size();
+    s.writeObject(map.comparator());
+    s.writeInt(pos);
+    while (--pos >= 0)
+      s.writeObject(itr.next());
   }
 
-  private void readObject(ObjectInputStream in)
+  /**
+   * Deserializes this object from the given stream.
+   *
+   * @param s the stream to read from
+   * @throws ClassNotFoundException if the underlying stream fails
+   * @throws IOException if the underlying stream fails
+   * @serialData the <i>comparator</i> (Object), followed by the set size
+   *             (int), the the elements in sorted order (Object)
+   */
+  private void readObject(ObjectInputStream s)
     throws IOException, ClassNotFoundException
   {
-    Comparator comparator = (Comparator) in.readObject();
-    int size = in.readInt();
-    TreeMap map = new TreeMap(comparator);    
-    map.putFromObjStream(in, size, false);
-    this.map = map;
+    s.defaultReadObject();
+    Comparator comparator = (Comparator) s.readObject();
+    int size = s.readInt();
+    map = new TreeMap(comparator);
+    ((TreeMap) map).putFromObjStream(s, size, false);
   }
 }
index cef84e51e720dc6aa08f50d90ffb25da4d4e5de9..24d80f8ca8fcf6e0977456ee96e62511c77fe1f9 100644 (file)
@@ -1,5 +1,5 @@
 /* Vector.java -- Class that provides growable arrays.
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -30,51 +30,73 @@ import java.lang.reflect.Array;
 import java.io.Serializable;
 
 /**
- * the <b>Vector</b> classes implements growable arrays of Objects.
+ * The <code>Vector</code> classes implements growable arrays of Objects.
  * You can access elements in a Vector with an index, just as you
  * can in a built in array, but Vectors can grow and shrink to accommodate
- * more or fewer objects.  
+ * more or fewer objects.<p>
  *
  * Vectors try to mantain efficiency in growing by having a
- * <b>capacityIncrement</b> that can be specified at instantiation.
+ * <code>capacityIncrement</code> that can be specified at instantiation.
  * When a Vector can no longer hold a new Object, it grows by the amount
- * in <b>capacityIncrement</b>.  
+ * in <code>capacityIncrement</code>. If this value is 0, the vector doubles in
+ * size.<p>
  *
- * Vector implements the JDK 1.2 List interface, and is therefor a fully
- * compliant Collection object.
+ * Vector implements the JDK 1.2 List interface, and is therefore a fully
+ * compliant Collection object. The iterators are fail-fast - if external
+ * code structurally modifies the vector, any operation on the iterator will
+ * then throw a {@link ConcurrentModificationException}. The Vector class is
+ * fully synchronized, but the iterators are not. So, when iterating over a
+ * vector, be sure to synchronize on the vector itself.  If you don't want the
+ * expense of synchronization, use ArrayList instead. On the other hand, the
+ * Enumeration of elements() is not thread-safe, nor is it fail-fast; so it
+ * can lead to undefined behavior even in a single thread if you modify the
+ * vector during iteration.<p>
+ *
+ * Note: Some methods, especially those specified by List, specify throwing
+ * {@link IndexOutOfBoundsException}, but it is easier to implement by
+ * throwing the subclass {@link ArrayIndexOutOfBoundsException}. Others
+ * directly specify this subclass.
  *
- * @specnote The JCL claims that various methods in this class throw
- * IndexOutOfBoundsException, which would be consistent with other collections
- * classes. ArrayIndexOutOfBoundsException is actually thrown, per the online 
- * docs, even for List method implementations.
- * 
  * @author Scott G. Miller
+ * @author Bryce McKinlay
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Collection
+ * @see List
+ * @see ArrayList
+ * @see LinkedList
+ * @since 1.0
+ * @status updated to 1.4
  */
-public class Vector extends AbstractList 
-  implements List, Cloneable, Serializable
+public class Vector extends AbstractList
+  implements List, RandomAccess, Cloneable, Serializable
 {
   /**
-   * The amount the Vector's internal array should be increased in size when
-   * a new element is added that exceeds the current size of the array,
-   * or when {@link #ensureCapacity} is called.
-   * @serial
+   * Compatible with JDK 1.0+.
    */
-  protected int capacityIncrement = 0;
+  private static final long serialVersionUID = -2767605614048989439L;
+
+  /**
+   * The internal array used to hold members of a Vector. The elements are
+   * in positions 0 through elementCount - 1, and all remaining slots are null.
+   * @serial the elements
+   */
+  protected Object[] elementData;
 
   /**
    * The number of elements currently in the vector, also returned by
    * {@link #size}.
-   * @serial
+   * @serial the size
    */
-  protected int elementCount = 0;
+  protected int elementCount;
 
   /**
-   * The internal array used to hold members of a Vector
-   * @serial
+   * The amount the Vector's internal array should be increased in size when
+   * a new element is added that exceeds the current size of the array,
+   * or when {@link #ensureCapacity} is called. If &lt;= 0, the vector just
+   * doubles in size.
+   * @serial the amount to grow the vector by
    */
-  protected Object[] elementData;
-
-  private static final long serialVersionUID = -2767605614048989439L;
+  protected int capacityIncrement;
 
   /**
    * Constructs an empty vector with an initial size of 10, and
@@ -82,36 +104,31 @@ public class Vector extends AbstractList
    */
   public Vector()
   {
-    this(10);
+    this(10, 0);
   }
 
   /**
    * Constructs a vector containing the contents of Collection, in the
-   * order given by the collection
+   * order given by the collection.
    *
-   * @param c A collection of elements to be added to the newly constructed
-   * vector
+   * @param c collection of elements to add to the new vector
+   * @throws NullPointerException if c is null
+   * @since 1.2
    */
   public Vector(Collection c)
   {
-    int csize = c.size();
-    elementData = new Object[csize];
-    elementCount = csize;
-    Iterator itr = c.iterator();
-    for (int i = 0; i < csize; i++)
-      {
-       elementData[i] = itr.next();
-      }
+    elementCount = c.size();
+    elementData = c.toArray(new Object[elementCount]);
   }
 
   /**
-   * Constructs a Vector with the initial capacity and capacity 
-   * increment specified
+   * Constructs a Vector with the initial capacity and capacity
+   * increment specified.
    *
-   * @param initialCapacity The initial size of the Vector's internal
-   * array
-   * @param capacityIncrement The amount the internal array should be
-   * increased if necessary
+   * @param initialCapacity the initial size of the Vector's internal array
+   * @param capacityIncrement the amount the internal array should be
+   *        increased by when necessary, 0 to double the size
+   * @throws IllegalArgumentException if initialCapacity &lt; 0
    */
   public Vector(int initialCapacity, int capacityIncrement)
   {
@@ -122,37 +139,37 @@ public class Vector extends AbstractList
   }
 
   /**
-   * Constructs a Vector with the initial capacity specified
+   * Constructs a Vector with the initial capacity specified, and a capacity
+   * increment of 0 (double in size).
    *
-   * @param initialCapacity The initial size of the Vector's internal array
+   * @param initialCapacity the initial size of the Vector's internal array
+   * @throws IllegalArgumentException if initialCapacity &lt; 0
    */
   public Vector(int initialCapacity)
   {
-    if (initialCapacity < 0)
-      throw new IllegalArgumentException();
-    elementData = new Object[initialCapacity];
+    this(initialCapacity, 0);
   }
 
   /**
-   * Copies the contents of a provided array into the Vector.  If the 
-   * array is too large to fit in the Vector, an ArrayIndexOutOfBoundsException
-   * is thrown.  Old elements in the Vector are overwritten by the new
-   * elements
+   * Copies the contents of a provided array into the Vector.  If the
+   * array is too large to fit in the Vector, an IndexOutOfBoundsException
+   * is thrown without modifying the array.  Old elements in the Vector are
+   * overwritten by the new elements.
    *
-   * @param anArray An array from which elements will be copied into the Vector
-   * 
-   * @throws ArrayIndexOutOfBoundsException the array being copied
-   * is larger than the Vectors internal data array
+   * @param a target array for the copy
+   * @throws IndexOutOfBoundsException the array is not large enough
+   * @throws NullPointerException the array is null
+   * @see #toArray(Object[])
    */
-  public synchronized void copyInto(Object[] anArray)
+  public synchronized void copyInto(Object[] a)
   {
-    System.arraycopy(elementData, 0, anArray, 0, elementCount);
+    System.arraycopy(elementData, 0, a, 0, elementCount);
   }
 
   /**
    * Trims the Vector down to size.  If the internal data array is larger
    * than the number of Objects its holding, a new array is constructed
-   * that precisely holds the elements.  
+   * that precisely holds the elements. Otherwise this does nothing.
    */
   public synchronized void trimToSize()
   {
@@ -166,68 +183,70 @@ public class Vector extends AbstractList
   }
 
   /**
-   * Ensures that <b>minCapacity</b> elements can fit within this Vector.
-   * If it cannot hold this many elements, the internal data array is expanded
-   * in the following manner.  If the current size plus the capacityIncrement
-   * is sufficient, the internal array is expanded by capacityIncrement.  
-   * If capacityIncrement is non-positive, the size is doubled.  If 
-   * neither is sufficient, the internal array is expanded to size minCapacity
+   * Ensures that <code>minCapacity</code> elements can fit within this Vector.
+   * If <code>elementData</code> is too small, it is expanded as follows:
+   * If the <code>elementCount + capacityIncrement</code> is adequate, that
+   * is the new size. If <code>capacityIncrement</code> is non-zero, the
+   * candidate size is double the current. If that is not enough, the new
+   * size is <code>minCapacity</code>.
    *
-   * @param minCapacity The minimum capacity the internal array should be
-   * able to handle after executing this method
+   * @param minCapacity the desired minimum capacity, negative values ignored
    */
   public synchronized void ensureCapacity(int minCapacity)
   {
     if (elementData.length >= minCapacity)
       return;
 
-    int newCapacity; 
+    int newCapacity;
     if (capacityIncrement <= 0)
       newCapacity = elementData.length * 2;
     else
       newCapacity = elementData.length + capacityIncrement;
-      
+
     Object[] newArray = new Object[Math.max(newCapacity, minCapacity)];
 
-    System.arraycopy(elementData, 0, newArray, 0, elementData.length);
+    System.arraycopy(elementData, 0, newArray, 0, elementCount);
     elementData = newArray;
   }
 
   /**
-   * Explicitly sets the size of the internal data array, copying the 
-   * old values to the new internal array.  If the new array is smaller
-   * than the old one, old values that don't fit are lost. If the new size
-   * is larger than the old one, the vector is padded with null entries.
+   * Explicitly sets the size of the vector (but not necessarily the size of
+   * the internal data array). If the new size is smaller than the old one,
+   * old values that don't fit are lost. If the new size is larger than the
+   * old one, the vector is padded with null entries.
    *
    * @param newSize The new size of the internal array
+   * @throws ArrayIndexOutOfBoundsException if the new size is negative
    */
   public synchronized void setSize(int newSize)
   {
+    // Don't bother checking for the case where size() == the capacity of the
+    // vector since that is a much less likely case; it's more efficient to
+    // not do the check and lose a bit of performance in that infrequent case
     modCount++;
-    Object[] newArray = new Object[newSize];
-    System.arraycopy(elementData, 0, newArray, 0, 
-                     Math.min(newSize, elementCount));
+    ensureCapacity(newSize);
+    if (newSize < elementCount)
+      Arrays.fill(elementData, newSize, elementCount, null);
     elementCount = newSize;
-    elementData = newArray;
   }
 
   /**
    * Returns the size of the internal data array (not the amount of elements
-   * contained in the Vector)
+   * contained in the Vector).
    *
-   * @returns capacity of the internal data array
+   * @return capacity of the internal data array
    */
-  public int capacity()
+  public synchronized int capacity()
   {
     return elementData.length;
   }
 
   /**
-   * Returns the number of elements stored in this Vector
+   * Returns the number of elements stored in this Vector.
    *
-   * @returns the number of elements in this Vector
+   * @return the number of elements in this Vector
    */
-  public int size()
+  public synchronized int size()
   {
     return elementCount;
   }
@@ -235,85 +254,89 @@ public class Vector extends AbstractList
   /**
    * Returns true if this Vector is empty, false otherwise
    *
-   * @returns true if the Vector is empty, false otherwise
+   * @return true if the Vector is empty, false otherwise
    */
-  public boolean isEmpty()
+  public synchronized boolean isEmpty()
   {
     return elementCount == 0;
   }
 
   /**
-   * Searches the vector starting at <b>index</b> for object <b>elem</b>
-   * and returns the index of the first occurrence of this Object.  If
-   * the object is not found, -1 is returned
+   * Returns an Enumeration of the elements of this Vector. The enumeration
+   * visits the elements in increasing index order, but is NOT thread-safe.
    *
-   * @param e The Object to search for
-   * @param index Start searching at this index
-   * @returns The index of the first occurrence of <b>elem</b>, or -1
-   * if it is not found
+   * @return an Enumeration
+   * @see #iterator()
    */
-  public synchronized int indexOf(Object e, int index)
+  // No need to synchronize as the Enumeration is not thread-safe!
+  public Enumeration elements()
   {
-    for (int i = index; i < elementCount; i++)
+    return new Enumeration()
+    {
+      private int i = 0;
+
+      public boolean hasMoreElements()
       {
-       if (e == null ? elementData[i] == null : e.equals(elementData[i]))
-         return i;
+        return i < elementCount;
       }
-    return -1;
+
+      public Object nextElement()
+      {
+        if (i >= elementCount)
+          throw new NoSuchElementException();
+        return elementData[i++];
+      }
+    };
   }
 
   /**
-   * Returns the first occurrence of <b>elem</b> in the Vector, or -1 if
-   * <b>elem</b> is not found.
+   * Returns true when <code>elem</code> is contained in this Vector.
    *
-   * @param elem The object to search for
-   * @returns The index of the first occurrence of <b>elem</b> or -1 if 
-   * not found
+   * @param elem the element to check
+   * @return true if the object is contained in this Vector, false otherwise
    */
-  public int indexOf(Object elem)
+  public boolean contains(Object elem)
   {
-    return indexOf(elem, 0);
+    return indexOf(elem, 0) >= 0;
   }
 
   /**
-   * Returns true if <b>elem</b> is contained in this Vector, false otherwise.
+   * Returns the first occurrence of <code>elem</code> in the Vector, or -1 if
+   * <code>elem</code> is not found.
    *
-   * @param elem The element to check
-   * @returns true if the object is contained in this Vector, false otherwise
+   * @param elem the object to search for
+   * @return the index of the first occurrence, or -1 if not found
    */
-  public boolean contains(Object elem)
+  public int indexOf(Object elem)
   {
-    return indexOf(elem, 0) != -1;
+    return indexOf(elem, 0);
   }
 
   /**
-   * Returns the index of the first occurrence of <b>elem</b>, when searching
-   * backwards from <b>index</b>.  If the object does not occur in this Vector,
-   * -1 is returned.
+   * Searches the vector starting at <code>index</code> for object
+   * <code>elem</code> and returns the index of the first occurrence of this
+   * Object.  If the object is not found, or index is larger than the size
+   * of the vector, -1 is returned.
    *
-   * @param eThe object to search for
-   * @param index The index to start searching in reverse from
-   * @returns The index of the Object if found, -1 otherwise
+   * @param e the Object to search for
+   * @param index start searching at this index
+   * @return the index of the next occurrence, or -1 if it is not found
+   * @throws IndexOutOfBoundsException if index &lt; 0
    */
-  public synchronized int lastIndexOf(Object e, int index)
+  public synchronized int indexOf(Object e, int index)
   {
-    if (index >= elementCount)
-      throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
-
-    for (int i = index; i >= 0; i--)
-      {
-       if (e == null ? elementData[i] == null : e.equals(elementData[i]))
-         return i;
-      }
+    for (int i = index; i < elementCount; i++)
+      if (equals(e, elementData[i]))
+        return i;
     return -1;
   }
 
   /**
-   * Returns the last index of <b>elem</b> within this Vector, or -1
-   * if the object is not within the Vector
+   * Returns the last index of <code>elem</code> within this Vector, or -1
+   * if the object is not within the Vector.
    *
-   * @param elem The object to search for
-   * @returns the last index of the object, or -1 if not found
+   * @param elem the object to search for
+   * @return the last index of the object, or -1 if not found
    */
   public int lastIndexOf(Object elem)
   {
@@ -321,29 +344,42 @@ public class Vector extends AbstractList
   }
 
   /**
-   * Returns the Object stored at <b>index</b>.  If index is out of range
-   * an ArrayIndexOutOfBoundsException is thrown.
+   * Returns the index of the first occurrence of <code>elem</code>, when
+   * searching backwards from <code>index</code>.  If the object does not
+   * occur in this Vector, or index is less than 0, -1 is returned.
+   *
+   * @param e the object to search for
+   * @param index the index to start searching in reverse from
+   * @return the index of the Object if found, -1 otherwise
+   * @throws IndexOutOfBoundsException if index &gt;= size()
+   */
+  public synchronized int lastIndexOf(Object e, int index)
+  {
+    checkBoundExclusive(index);
+    for (int i = index; i >= 0; i--)
+      if (equals(e, elementData[i]))
+        return i;
+    return -1;
+  }
+
+  /**
+   * Returns the Object stored at <code>index</code>.
    *
    * @param index the index of the Object to retrieve
-   * @returns The object at <b>index</b>
-   * @throws ArrayIndexOutOfBoundsException <b>index</b> is
-   * larger than the Vector
+   * @return the object at <code>index</code>
+   * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
+   * @see #get(int)
    */
   public synchronized Object elementAt(int index)
   {
-    //Within the bounds of this Vector does not necessarily mean within 
-    //the bounds of the internal array
-    if (index >= elementCount)
-      throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
-
+    checkBoundExclusive(index);
     return elementData[index];
   }
 
   /**
-   * Returns the first element in the Vector.  If there is no first Object 
-   * (The vector is empty), a NoSuchElementException is thrown.
+   * Returns the first element (index 0) in the Vector.
    *
-   * @returns The first Object in the Vector
+   * @return the first Object in the Vector
    * @throws NoSuchElementException the Vector is empty
    */
   public synchronized Object firstElement()
@@ -351,14 +387,13 @@ public class Vector extends AbstractList
     if (elementCount == 0)
       throw new NoSuchElementException();
 
-    return elementAt(0);
+    return elementData[0];
   }
 
   /**
-   * Returns the last element in the Vector.  If the Vector has no last element
-   * (The vector is empty), a NoSuchElementException is thrown.
+   * Returns the last element in the Vector.
    *
-   * @returns The last Object in the Vector
+   * @return the last Object in the Vector
    * @throws NoSuchElementException the Vector is empty
    */
   public synchronized Object lastElement()
@@ -366,95 +401,61 @@ public class Vector extends AbstractList
     if (elementCount == 0)
       throw new NoSuchElementException();
 
-    return elementAt(elementCount - 1);
-  }
-
-  /**
-   * Places <b>obj</b> at <b>index</b> within the Vector.  If <b>index</b>
-   * refers to an index outside the Vector, an ArrayIndexOutOfBoundsException
-   * is thrown.  
-   * 
-   * @param obj The object to store
-   * @param index The position in the Vector to store the object
-   * @throws ArrayIndexOutOfBoundsException the index is out of range
-   */
-  public synchronized void setElementAt(Object obj, int index)
-  {
-    if (index >= elementCount)
-      throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
-
-    elementData[index] = obj;
+    return elementData[elementCount - 1];
   }
 
   /**
-   * Puts <b>element</b> into the Vector at position <b>index</b> and returns
-   * the Object that previously occupied that position.
+   * Changes the element at <code>index</code> to be <code>obj</code>
    *
-   * @param index The index within the Vector to place the Object
-   * @param element The Object to store in the Vector
-   * @returns The previous object at the specified index
+   * @param obj the object to store
+   * @param index the position in the Vector to store the object
    * @throws ArrayIndexOutOfBoundsException the index is out of range
-   *
+   * @see #set(int, Object)
    */
-  public synchronized Object set(int index, Object element)
+  public void setElementAt(Object obj, int index)
   {
-    if (index >= elementCount)
-      throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
-
-    Object temp = elementData[index];
-    elementData[index] = element;
-    return temp;
+    set(index, obj);
   }
 
   /**
-   * Removes the element at <b>index</b>, and shifts all elements at
-   * positions greater than index to their index - 1.  
+   * Removes the element at <code>index</code>, and shifts all elements at
+   * positions greater than index to their index - 1.
    *
-   * @param index The index of the element to remove
+   * @param index the index of the element to remove
+   * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size();
+   * @see #remove(int)
    */
-  public synchronized void removeElementAt(int index)
+  public void removeElementAt(int index)
   {
-    if (index >= elementCount)
-      throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
-
-    modCount++;
-    elementCount--;
-    if (index < elementCount)
-      System.arraycopy(elementData, index + 1, elementData, index,
-                      elementCount - index);
-    //Delete the last element (which has been copied back one index)
-    //so it can be garbage collected;
-    elementData[elementCount] = null;
+    remove(index);
   }
 
   /**
-   * Inserts a new element into the Vector at <b>index</b>.  Any elements
+   * Inserts a new element into the Vector at <code>index</code>.  Any elements
    * at or greater than index are shifted up one position.
    *
-   * @param obj The object to insert
-   * @param index The index at which the object is inserted
+   * @param obj the object to insert
+   * @param index the index at which the object is inserted
+   * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt; size()
+   * @see #add(int, Object)
    */
-  public void insertElementAt(Object obj, int index)
+  public synchronized void insertElementAt(Object obj, int index)
   {
-    if (index > elementCount)
-      throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
-
+    checkBoundInclusive(index);
     if (elementCount == elementData.length)
       ensureCapacity(elementCount + 1);
-    ++modCount;
-    ++elementCount;
+    modCount++;
     System.arraycopy(elementData, index, elementData, index + 1,
-                    elementCount - 1 - index);
+                     elementCount - index);
+    elementCount++;
     elementData[index] = obj;
   }
 
   /**
-   * Adds an element to the Vector at the end of the Vector.  If the vector
-   * cannot hold the element with its present capacity, its size is increased
-   * based on the same rules followed if ensureCapacity was called with the
-   * argument currentSize+1.
+   * Adds an element to the Vector at the end of the Vector.  The vector
+   * is increased by ensureCapacity(size() + 1) if needed.
    *
-   * @param obj The object to add to the Vector
+   * @param obj the object to add to the Vector
    */
   public synchronized void addElement(Object obj)
   {
@@ -465,20 +466,21 @@ public class Vector extends AbstractList
   }
 
   /**
-   * Removes the first occurrence of the given object from the Vector.
-   * If such a remove was performed (the object was found), true is returned.
-   * If there was no such object, false is returned.
+   * Removes the first (the lowestindex) occurance of the given object from
+   * the Vector. If such a remove was performed (the object was found), true
+   * is returned. If there was no such object, false is returned.
    *
-   * @param obj The object to remove from the Vector
-   * @returns true if the Object was in the Vector, false otherwise
+   * @param obj the object to remove from the Vector
+   * @return true if the Object was in the Vector, false otherwise
+   * @see #remove(Object)
    */
   public synchronized boolean removeElement(Object obj)
   {
-    int idx = indexOf(obj);
-    if (idx != -1)
+    int idx = indexOf(obj, 0);
+    if (idx >= 0)
       {
-       removeElementAt(idx);
-       return true;
+        remove(idx);
+        return true;
       }
     return false;
   }
@@ -486,46 +488,49 @@ public class Vector extends AbstractList
   /**
    * Removes all elements from the Vector.  Note that this does not
    * resize the internal data array.
+   *
+   * @see #clear()
    */
   public synchronized void removeAllElements()
   {
-    modCount++;
     if (elementCount == 0)
       return;
 
-    for (int i = elementCount - 1; i >= 0; --i)
-      {
-       elementData[i] = null;
-      }
+    modCount++;
+    Arrays.fill(elementData, 0, elementCount, null);
     elementCount = 0;
   }
 
   /**
-   * Creates a new Vector with the same contents as this one.
+   * Creates a new Vector with the same contents as this one. The clone is
+   * shallow; elements are not cloned.
+   *
+   * @return the clone of this vector
    */
   public synchronized Object clone()
   {
     try
       {
-       Vector clone = (Vector) super.clone();
-       clone.elementData = (Object[]) elementData.clone();
-       return clone;
+        Vector clone = (Vector) super.clone();
+        clone.elementData = (Object[]) elementData.clone();
+        return clone;
       }
     catch (CloneNotSupportedException ex)
       {
-       throw new InternalError(ex.toString());
+        // Impossible to get here.
+        throw new InternalError(ex.toString());
       }
   }
 
   /**
    * Returns an Object array with the contents of this Vector, in the order
-   * they are stored within this Vector.  Note that the Object array returned 
-   * is not the internal data array, and that it holds only the elements 
-   * within the Vector.  This is similar to creating a new Object[] with the 
+   * they are stored within this Vector.  Note that the Object array returned
+   * is not the internal data array, and that it holds only the elements
+   * within the Vector.  This is similar to creating a new Object[] with the
    * size of this Vector, then calling Vector.copyInto(yourArray).
    *
-   * @returns An Object[] containing the contents of this Vector in order
-   *
+   * @return an Object[] containing the contents of this Vector in order
+   * @since 1.2
    */
   public synchronized Object[] toArray()
   {
@@ -535,76 +540,97 @@ public class Vector extends AbstractList
   }
 
   /**
-   * Returns an array containing the contents of this Vector.  
+   * Returns an array containing the contents of this Vector.
    * If the provided array is large enough, the contents are copied
-   * into that array, and a null is placed in the position size(). 
+   * into that array, and a null is placed in the position size().
    * In this manner, you can obtain the size of a Vector by the position
-   * of the null element.  If the type of the provided array cannot 
-   * hold the elements, an ArrayStoreException is thrown.
-   * 
-   * If the provided array is not large enough,
-   * a new one is created with the contents of the Vector, and no null 
-   * element.  The new array is of the same runtime type as the provided
-   * array.
+   * of the null element, if you know the vector does not itself contain
+   * null entries.  If the array is not large enough, reflection is used
+   * to create a bigger one of the same runtime type.
    *
-   *
-   * @param array An array to copy the Vector into if large enough
-   * @returns An array with the contents of this Vector in order
+   * @param a an array to copy the Vector into if large enough
+   * @return an array with the contents of this Vector in order
    * @throws ArrayStoreException the runtime type of the provided array
-   * cannot hold the elements of the Vector
+   *         cannot hold the elements of the Vector
+   * @throws NullPointerException if <code>a</code> is null
+   * @since 1.2
    */
-  public synchronized Object[] toArray(Object[] array)
+  public synchronized Object[] toArray(Object[] a)
   {
-    if (array.length < elementCount)
-      array = (Object[]) Array.newInstance(array.getClass().getComponentType(), 
-                                          elementCount);
-    else if (array.length > elementCount)
-      array[elementCount] = null;
-    System.arraycopy(elementData, 0, array, 0, elementCount);
-    return array;
+    if (a.length < elementCount)
+      a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
+                                       elementCount);
+    else if (a.length > elementCount)
+      a[elementCount] = null;
+    System.arraycopy(elementData, 0, a, 0, elementCount);
+    return a;
   }
 
   /**
-   * Returns the element at position <b>index</b>
+   * Returns the element at position <code>index</code>.
    *
    * @param index the position from which an element will be retrieved
-   * @throws ArrayIndexOutOfBoundsException the index is not within the 
-   * range of the Vector
+   * @return the element at that position
+   * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
+   * @since 1.2
    */
-  public synchronized Object get(int index)
+  public Object get(int index)
   {
     return elementAt(index);
   }
 
   /**
-   * Removes the given Object from the Vector.  If it exists, true
-   * is returned, if not, false is returned.
+   * Puts <code>element</code> into the Vector at position <code>index</code>
+   * and returns the Object that previously occupied that position.
    *
-   * @param o The object to remove from the Vector
-   * @returns true if the Object existed in the Vector, false otherwise
+   * @param index the index within the Vector to place the Object
+   * @param element the Object to store in the Vector
+   * @return the previous object at the specified index
+   * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
+   * @since 1.2
    */
-  public boolean remove(Object o)
+  public synchronized Object set(int index, Object element)
   {
-    return removeElement(o);
+    checkBoundExclusive(index);
+    Object temp = elementData[index];
+    elementData[index] = element;
+    return temp;
   }
 
   /**
    * Adds an object to the Vector.
    *
-   * @param o The element to add to the Vector
+   * @param o the element to add to the Vector
+   * @return true, as specified by List
+   * @since 1.2
    */
-  public synchronized boolean add(Object o)
+  public boolean add(Object o)
   {
     addElement(o);
     return true;
   }
 
+  /**
+   * Removes the given Object from the Vector.  If it exists, true
+   * is returned, if not, false is returned.
+   *
+   * @param o the object to remove from the Vector
+   * @return true if the Object existed in the Vector, false otherwise
+   * @since 1.2
+   */
+  public boolean remove(Object o)
+  {
+    return removeElement(o);
+  }
+
   /**
    * Adds an object at the specified index.  Elements at or above
    * index are shifted up one position.
    *
-   * @param index The index at which to add the element
-   * @param element The element to add to the Vector
+   * @param index the index at which to add the element
+   * @param element the element to add to the Vector
+   * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt; size()
+   * @since 1.2
    */
   public void add(int index, Object element)
   {
@@ -614,153 +640,253 @@ public class Vector extends AbstractList
   /**
    * Removes the element at the specified index, and returns it.
    *
-   * @param index The position from which to remove the element
-   * @returns The object removed
-   * @throws ArrayIndexOutOfBoundsException the index was out of the range
-   * of the Vector
+   * @param index the position from which to remove the element
+   * @return the object removed
+   * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
+   * @since 1.2
    */
   public synchronized Object remove(int index)
   {
-    if (index >= elementCount)
-      throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
-  
+    checkBoundExclusive(index);
     Object temp = elementData[index];
-    removeElementAt(index);
+    modCount++;
+    elementCount--;
+    if (index < elementCount)
+      System.arraycopy(elementData, index + 1, elementData, index,
+                       elementCount - index);
+    elementData[elementCount] = null;
     return temp;
   }
 
   /**
-   * Clears all elements in the Vector and sets its size to 0
+   * Clears all elements in the Vector and sets its size to 0.
    */
   public void clear()
   {
     removeAllElements();
   }
 
+  /**
+   * Returns true if this Vector contains all the elements in c.
+   *
+   * @param c the collection to compare to
+   * @return true if this vector contains all elements of c
+   * @throws NullPointerException if c is null
+   * @since 1.2
+   */
   public synchronized boolean containsAll(Collection c)
   {
-    Iterator itr = c.iterator();
-    int size = c.size();
-    for (int pos = 0; pos < size; pos++)
-      {
-       if (!contains(itr.next()))
-         return false;
-      }
-    return true;
+    // Here just for the sychronization.
+    return super.containsAll(c);
   }
 
+  /**
+   * Appends all elements of the given collection to the end of this Vector.
+   * Behavior is undefined if the collection is modified during this operation
+   * (for example, if this == c).
+   *
+   * @param c the collection to append
+   * @return true if this vector changed, in other words c was not empty
+   * @throws NullPointerException if c is null
+   * @since 1.2
+   */
   public synchronized boolean addAll(Collection c)
   {
     return addAll(elementCount, c);
   }
-  
+
+  /**
+   * Remove from this vector all elements contained in the given collection.
+   *
+   * @param c the collection to filter out
+   * @return true if this vector changed
+   * @throws NullPointerException if c is null
+   * @since 1.2
+   */
   public synchronized boolean removeAll(Collection c)
   {
-    return super.removeAll(c);
+    int i;
+    int j;
+    for (i = 0; i < elementCount; i++)
+      if (c.contains(elementData[i]))
+        break;
+    if (i == elementCount)
+      return false;
+
+    modCount++;
+    for (j = i++; i < elementCount; i++)
+      if (! c.contains(elementData[i]))
+        elementData[j++] = elementData[i];
+    elementCount -= i - j;
+    return true;
   }
-  
+
+  /**
+   * Retain in this vector only the elements contained in the given collection.
+   *
+   * @param c the collection to filter by
+   * @return true if this vector changed
+   * @throws NullPointerException if c is null
+   * @since 1.2
+   */
   public synchronized boolean retainAll(Collection c)
   {
-    return super.retainAll(c);
+    int i;
+    int j;
+    for (i = 0; i < elementCount; i++)
+      if (! c.contains(elementData[i]))
+        break;
+    if (i == elementCount)
+      return false;
+
+    modCount++;
+    for (j = i++; i < elementCount; i++)
+      if (c.contains(elementData[i]))
+        elementData[j++] = elementData[i];
+    elementCount -= i - j;
+    return true;
   }
 
+  /**
+   * Inserts all elements of the given collection at the given index of
+   * this Vector. Behavior is undefined if the collection is modified during
+   * this operation (for example, if this == c).
+   *
+   * @param c the collection to append
+   * @return true if this vector changed, in other words c was not empty
+   * @throws NullPointerException if c is null
+   * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt; size()
+   * @since 1.2
+   */
   public synchronized boolean addAll(int index, Collection c)
   {
-    if (index < 0 || index > elementCount)
-      throw new ArrayIndexOutOfBoundsException(index);
-    modCount++;
+    checkBoundInclusive(index);
     Iterator itr = c.iterator();
     int csize = c.size();
 
+    modCount++;
     ensureCapacity(elementCount + csize);
     int end = index + csize;
     if (elementCount > 0 && index != elementCount)
       System.arraycopy(elementData, index, elementData, end, csize);
     elementCount += csize;
-    for (; index < end; index++)
-      {
-        elementData[index] = itr.next();
-      }
-    return (csize > 0);  
+    for ( ; index < end; index++)
+      elementData[index] = itr.next();
+    return (csize > 0);
   }
 
-  public synchronized boolean equals(Object c)
+  /**
+   * Compares this to the given object.
+   *
+   * @param o the object to compare to
+   * @return true if the two are equal
+   * @since 1.2
+   */
+  public synchronized boolean equals(Object o)
   {
-    return super.equals(c);
+    // Here just for the sychronization.
+    return super.equals(o);
   }
 
+  /**
+   * Computes the hashcode of this object.
+   *
+   * @return the hashcode
+   * @since 1.2
+   */
   public synchronized int hashCode()
   {
+    // Here just for the sychronization.
     return super.hashCode();
   }
 
   /**
-   * Returns a string representation of this Vector in the form 
-   * [element0, element1, ... elementN]
+   * Returns a string representation of this Vector in the form
+   * "[element0, element1, ... elementN]".
    *
-   * @returns the String representation of this Vector
+   * @return the String representation of this Vector
    */
   public synchronized String toString()
   {
-    String r = "[";
-    for (int i = 0; i < elementCount; i++)
-      {
-       r += elementData[i];
-       if (i < elementCount - 1)
-         r += ", ";
-      }
-    r += "]";
-    return r;
+    // Here just for the sychronization.
+    return super.toString();
   }
 
   /**
-   * Returns an Enumeration of the elements of this List.
-   * The Enumeration returned is compatible behavior-wise with
-   * the 1.1 elements() method, in that it does not check for
-   * concurrent modification.
+   * Obtain a List view of a subsection of this list, from fromIndex
+   * (inclusive) to toIndex (exclusive). If the two indices are equal, the
+   * sublist is empty. The returned list is modifiable, and changes in one
+   * reflect in the other. If this list is structurally modified in
+   * any way other than through the returned list, the result of any subsequent
+   * operations on the returned list is undefined.
+   * <p>
    *
-   * @returns an Enumeration
+   * @param fromIndex the index that the returned list should start from
+   *        (inclusive)
+   * @param toIndex the index that the returned list should go to (exclusive)
+   * @return a List backed by a subsection of this vector
+   * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+   *         || toIndex &gt; size()
+   * @throws IllegalArgumentException if fromIndex &gt; toIndex
+   * @see ConcurrentModificationException
+   * @since 1.2
    */
-  public synchronized Enumeration elements()
-  {
-    return new Enumeration()
-    {
-      int i = 0;
-      public boolean hasMoreElements()
-      {
-       return (i < elementCount);
-      }
-      public Object nextElement()
-      {
-       if (i >= elementCount)
-         throw new NoSuchElementException();
-       return (elementAt(i++));
-      }
-    };
-  }
-  
-  public List subList(int fromIndex, int toIndex)
+  public synchronized List subList(int fromIndex, int toIndex)
   {
     List sub = super.subList(fromIndex, toIndex);
-    return Collections.synchronizedList(sub);
+    // We must specify the correct object to synchronize upon, hence the
+    // use of a non-public API
+    return new Collections.SynchronizedList(this, sub);
   }
-  
-  /** @specnote This is not specified as synchronized in the JCL, but it seems
-    * to me that is should be. If it isn't, a clear() operation on a sublist
-    * will not be synchronized w.r.t. the Vector object.
-    */
-  protected synchronized void removeRange(int fromIndex, int toIndex)
+
+  /**
+   * Removes a range of elements from this list.
+   *
+   * @param fromIndex the index to start deleting from (inclusive)
+   * @param toIndex the index to delete up to (exclusive)
+   */
+  // This does not need to be synchronized, because it is only called through
+  // clear() of a sublist, and clear() had already synchronized.
+  protected void removeRange(int fromIndex, int toIndex)
   {
-    modCount++;
     if (fromIndex != toIndex)
       {
-       System.arraycopy(elementData, toIndex, elementData, fromIndex, 
-                        elementCount - toIndex);
-       // Clear unused elements so objects can be collected.
-       int save = elementCount;
-       elementCount -= (toIndex - fromIndex);
-       for (int i = elementCount; i < save; ++i)
-         elementData[i] = null;
+        modCount++;
+        System.arraycopy(elementData, toIndex, elementData, fromIndex,
+                         elementCount - toIndex);
+        int save = elementCount;
+        elementCount -= toIndex - fromIndex;
+        Arrays.fill(elementData, elementCount, save, null);
       }
   }
+
+  /**
+   * Checks that the index is in the range of possible elements (inclusive).
+   *
+   * @param index the index to check
+   * @throws ArrayIndexOutOfBoundsException if index &gt; size
+   */
+  private void checkBoundInclusive(int index)
+  {
+    // Implementation note: we do not check for negative ranges here, since
+    // use of a negative index will cause an ArrayIndexOutOfBoundsException
+    // with no effort on our part.
+    if (index > elementCount)
+      throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
+  }
+
+  /**
+   * Checks that the index is in the range of existing elements (exclusive).
+   *
+   * @param index the index to check
+   * @throws ArrayIndexOutOfBoundsException if index &gt;= size
+   */
+  private void checkBoundExclusive(int index)
+  {
+    // Implementation note: we do not check for negative ranges here, since
+    // use of a negative index will cause an ArrayIndexOutOfBoundsException
+    // with no effort on our part.
+    if (index >= elementCount)
+      throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
+  }
 }
index 6f39d46810873377d04e7137f740373cab76fd8c..6366e9822c2f39535ca8d0f4803ef408d406cf8c 100644 (file)
@@ -1,5 +1,6 @@
-/* java.util.WeakHashMap
-   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+/* java.util.WeakHashMap -- a hashtable that keeps only weak references
+   to its keys, allowing the virtual machine to reclaim them
+   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -26,6 +27,7 @@ executable file might be covered by the GNU General Public License. */
 
 
 package java.util;
+
 import java.lang.ref.WeakReference;
 import java.lang.ref.ReferenceQueue;
 
@@ -50,53 +52,80 @@ import java.lang.ref.ReferenceQueue;
  * has similar phenomenons: The size may spontaneously shrink, or an
  * entry, that was in the set before, suddenly disappears. <br>
  *
- * A weak hash map is not meant for caches; use a normal map, with 
- * soft references as values instead.  <br>
+ * A weak hash map is not meant for caches; use a normal map, with
+ * soft references as values instead, or try {@link LinkedHashMap}.  <br>
  *
- * The weak hash map supports null values and null keys.  Null keys
- * are never deleted from the map (except explictly of course). 
+ * The weak hash map supports null values and null keys.  The null key
+ * is never deleted from the map (except explictly of course).
  * The performance of the methods are similar to that of a hash map. <br>
  *
- * The value object are strongly referenced by this table.  So if a
+ * The value objects are strongly referenced by this table.  So if a
  * value object maintains a strong reference to the key (either direct
  * or indirect) the key will never be removed from this map.  According
- * to Sun, this problem may be fixed in a future release.  It is not 
+ * to Sun, this problem may be fixed in a future release.  It is not
  * possible to do it with the jdk 1.2 reference model, though.
  *
- * @since jdk1.2
- * @author Jochen Hoenicke 
+ * @author Jochen Hoenicke
+ * @author Eric Blake <ebb9@email.byu.edu>
  * @see HashMap
- * @see WeakReference */
+ * @see WeakReference
+ * @see LinkedHashMap
+ * @since 1.2
+ * @status updated to 1.4
+ */
 public class WeakHashMap extends AbstractMap implements Map
 {
-  /** 
-   * The default capacity for an instance of HashMap.  
+  /**
+   * The default capacity for an instance of HashMap.
    * Sun's documentation mildly suggests that this (11) is the correct
-   * value.  
+   * value.
    */
   private static final int DEFAULT_CAPACITY = 11;
 
-  /** 
-   * The default load factor of a HashMap 
+  /**
+   * The default load factor of a HashMap.
    */
   private static final float DEFAULT_LOAD_FACTOR = 0.75F;
 
   /**
    * This is used instead of the key value <i>null</i>.  It is needed
-   * to distinguish between an null key and a removed key.  
+   * to distinguish between an null key and a removed key.
    */
-  private static final Object NULL_KEY = new Object();
+  // Package visible for use by nested classes.
+  static final Object NULL_KEY = new Object()
+  {
+    /**
+     * Sets the hashCode to 0, since that's what null would map to.
+     * @return the hash code 0
+     */
+    public int hashCode()
+    {
+      return 0;
+    }
+
+    /**
+     * Compares this key to the given object. Normally, an object should
+     * NEVER compare equal to null, but since we don't publicize NULL_VALUE,
+     * it saves bytecode to do so here.
+     * @return true iff o is this or null
+     */
+    public boolean equals(Object o)
+    {
+      return null == o || this == o;
+    }
+  };
 
   /**
    * The reference queue where our buckets (which are WeakReferences) are
    * registered to.
    */
-  private ReferenceQueue queue;
+  private final ReferenceQueue queue;
 
   /**
    * The number of entries in this hash map.
    */
-  private int size;
+  // Package visible for use by nested classes.
+  int size;
 
   /**
    * The load factor of this WeakHashMap.  This is the maximum ratio of
@@ -108,7 +137,7 @@ public class WeakHashMap extends AbstractMap implements Map
   /**
    * The rounded product of the capacity (i.e. number of buckets) and
    * the load factor. When the number of elements exceeds the
-   * threshold, the HashMap calls <pre>rehash()</pre>.  
+   * threshold, the HashMap calls <pre>rehash()</pre>.
    */
   private int threshold;
 
@@ -119,17 +148,20 @@ public class WeakHashMap extends AbstractMap implements Map
    * by the garbage collection.  Instead the iterators must make
    * sure to have strong references to the entries they rely on.
    */
-  private int modCount;
+  // Package visible for use by nested classes.
+  int modCount;
 
-  /** 
+  /**
    * The entry set.  There is only one instance per hashmap, namely
    * theEntrySet.  Note that the entry set may silently shrink, just
    * like the WeakHashMap.
    */
-  private class WeakEntrySet extends AbstractSet
+  private final class WeakEntrySet extends AbstractSet
   {
     /**
-     * Returns the size of this set. 
+     * Returns the size of this set.
+     *
+     * @return the set size
      */
     public int size()
     {
@@ -138,151 +170,150 @@ public class WeakHashMap extends AbstractMap implements Map
 
     /**
      * Returns an iterator for all entries.
+     *
+     * @return an Entry iterator
      */
     public Iterator iterator()
     {
       return new Iterator()
       {
-       /** 
-        * The entry that was returned by the last
-        * <code>next()</code> call.  This is also the entry whose
-        * bucket should be removed by the <code>remove</code> call. <br>
-        *
-        * It is null, if the <code>next</code> method wasn't 
-        * called yet, or if the entry was already removed.  <br>
-        *
-        * Remembering this entry here will also prevent it from
-        * being removed under us, since the entry strongly refers
-        * to the key.
-        */
-       WeakBucket.Entry lastEntry;
-
-       /** 
-        * The entry that will be returned by the next
-        * <code>next()</code> call.  It is <code>null</code> if there
-        * is no further entry. <br>
-        *
-        * Remembering this entry here will also prevent it from
-        * being removed under us, since the entry strongly refers
-        * to the key.
-        */
-       WeakBucket.Entry nextEntry = findNext(null);
-
-       /**
-        * The known number of modification to the list, if it differs
-        * from the real number, we through an exception.
-        */
-       int knownMod = modCount;
-
-       /** 
-        * Check the known number of modification to the number of
-        * modifications of the table.  If it differs from the real
-        * number, we throw an exception.
-        * @exception ConcurrentModificationException if the number
-        * of modifications doesn't match.
-        */
-       private void checkMod()
-       {
-         /* This method will get inlined */
-         if (knownMod != modCount)
-           throw new ConcurrentModificationException();
-       }
-
-       /**
-        * Get a strong reference to the next entry after
-        * lastBucket.
-        * @param lastBucket the previous bucket, or null if we should
-        * get the first entry.
-        * @return the next entry.
-        */
-       private WeakBucket.Entry findNext(WeakBucket.Entry lastEntry)
-       {
-         int slot;
-         WeakBucket nextBucket;
-         if (lastEntry != null)
-           {
-             nextBucket = lastEntry.getBucket().next;
-             slot = lastEntry.getBucket().slot;
-           }
-         else
-           {
-             nextBucket = buckets[0];
-             slot = 0;
-           }
-
-         while (true)
-           {
-             while (nextBucket != null)
-               {
-                 WeakBucket.Entry entry = nextBucket.getEntry();
-                 if (entry != null)
-                   /* This is the next entry */
-                   return entry;
-
-                 /* entry was cleared, try next */
-                 nextBucket = nextBucket.next;
-               }
-
-             slot++;
-             if (slot == buckets.length)
-               /* No more buckets, we are through */
-               return null;
-
-             nextBucket = buckets[slot];
-           }
-       }
-
-
-       /**
-        * Checks if there are more entries.
-        * @return true, iff there are more elements.
-        * @exception IllegalModificationException if the hash map was
-        * modified.
-        */
-       public boolean hasNext()
-       {
-         cleanQueue();
-         checkMod();
-         return (nextEntry != null);
-       }
-
-       /**
-        * Returns the next entry.
-        * @return the next entry.
-        * @exception IllegalModificationException if the hash map was
-        * modified.
-        * @exception NoSuchElementException if there is no entry.
-        */
-       public Object next()
-       {
-         cleanQueue();
-         checkMod();
-         if (nextEntry == null)
-           throw new NoSuchElementException();
-         lastEntry = nextEntry;
-         nextEntry = findNext(lastEntry);
-         return lastEntry;
-       }
-
-       /**
-        * Removes the last returned entry from this set.  This will
-        * also remove the bucket of the underlying weak hash map.
-        * @exception IllegalModificationException if the hash map was
-        * modified.
-        * @exception IllegalStateException if <code>next()</code> was
-        * never called or the element was already removed. 
-        */
-       public void remove()
-       {
-         cleanQueue();
-         checkMod();
-         if (lastEntry == null)
-           throw new IllegalStateException();
-         internalRemove(lastEntry.getBucket());
-         lastEntry = null;
-         modCount++;
-         knownMod = modCount;
-       }
+        /**
+         * The entry that was returned by the last
+         * <code>next()</code> call.  This is also the entry whose
+         * bucket should be removed by the <code>remove</code> call. <br>
+         *
+         * It is null, if the <code>next</code> method wasn't
+         * called yet, or if the entry was already removed.  <br>
+         *
+         * Remembering this entry here will also prevent it from
+         * being removed under us, since the entry strongly refers
+         * to the key.
+         */
+        WeakBucket.WeakEntry lastEntry;
+
+        /**
+         * The entry that will be returned by the next
+         * <code>next()</code> call.  It is <code>null</code> if there
+         * is no further entry. <br>
+         *
+         * Remembering this entry here will also prevent it from
+         * being removed under us, since the entry strongly refers
+         * to the key.
+         */
+        WeakBucket.WeakEntry nextEntry = findNext(null);
+
+        /**
+         * The known number of modification to the list, if it differs
+         * from the real number, we throw an exception.
+         */
+        int knownMod = modCount;
+
+        /**
+         * Check the known number of modification to the number of
+         * modifications of the table.  If it differs from the real
+         * number, we throw an exception.
+         * @throws ConcurrentModificationException if the number
+         *         of modifications doesn't match.
+         */
+        private void checkMod()
+        {
+          // This method will get inlined.
+          cleanQueue();
+          if (knownMod != modCount)
+            throw new ConcurrentModificationException();
+        }
+
+        /**
+         * Get a strong reference to the next entry after
+         * lastBucket.
+         * @param lastEntry the previous bucket, or null if we should
+         * get the first entry.
+         * @return the next entry.
+         */
+        private WeakBucket.WeakEntry findNext(WeakBucket.WeakEntry lastEntry)
+        {
+          int slot;
+          WeakBucket nextBucket;
+          if (lastEntry != null)
+            {
+              nextBucket = lastEntry.getBucket().next;
+              slot = lastEntry.getBucket().slot;
+            }
+          else
+            {
+              nextBucket = buckets[0];
+              slot = 0;
+            }
+
+          while (true)
+            {
+              while (nextBucket != null)
+                {
+                  WeakBucket.WeakEntry entry = nextBucket.getEntry();
+                  if (entry != null)
+                    // This is the next entry.
+                    return entry;
+
+                  // Entry was cleared, try next.
+                  nextBucket = nextBucket.next;
+                }
+
+              slot++;
+              if (slot == buckets.length)
+                // No more buckets, we are through.
+                return null;
+
+              nextBucket = buckets[slot];
+            }
+        }
+
+        /**
+         * Checks if there are more entries.
+         * @return true, iff there are more elements.
+         * @throws ConcurrentModificationException if the hash map was
+         *         modified.
+         */
+        public boolean hasNext()
+        {
+          checkMod();
+          return nextEntry != null;
+        }
+
+        /**
+         * Returns the next entry.
+         * @return the next entry.
+         * @throws ConcurrentModificationException if the hash map was
+         *         modified.
+         * @throws NoSuchElementException if there is no entry.
+         */
+        public Object next()
+        {
+          checkMod();
+          if (nextEntry == null)
+            throw new NoSuchElementException();
+          lastEntry = nextEntry;
+          nextEntry = findNext(lastEntry);
+          return lastEntry;
+        }
+
+        /**
+         * Removes the last returned entry from this set.  This will
+         * also remove the bucket of the underlying weak hash map.
+         * @throws ConcurrentModificationException if the hash map was
+         *         modified.
+         * @throws IllegalStateException if <code>next()</code> was
+         *         never called or the element was already removed.
+         */
+        public void remove()
+        {
+          checkMod();
+          if (lastEntry == null)
+            throw new IllegalStateException();
+          modCount++;
+          internalRemove(lastEntry.getBucket());
+          lastEntry = null;
+          knownMod++;
+        }
       };
     }
   }
@@ -293,28 +324,28 @@ public class WeakHashMap extends AbstractMap implements Map
    * number. <br>
    *
    * It would be cleaner to have a WeakReference as field, instead of
-   * extending it, but if a weak reference get cleared, we only get
+   * extending it, but if a weak reference gets cleared, we only get
    * the weak reference (by queue.poll) and wouldn't know where to
    * look for this reference in the hashtable, to remove that entry.
    *
-   * @author Jochen Hoenicke 
+   * @author Jochen Hoenicke
    */
   private static class WeakBucket extends WeakReference
   {
     /**
      * The value of this entry.  The key is stored in the weak
-     * reference that we extend.  
+     * reference that we extend.
      */
     Object value;
 
     /**
      * The next bucket describing another entry that uses the same
-     * slot.  
+     * slot.
      */
     WeakBucket next;
 
     /**
-     * The slot of this entry. This should be 
+     * The slot of this entry. This should be
      * <pre>
      *  Math.abs(key.hashCode() % buckets.length)
      * </pre>
@@ -329,12 +360,13 @@ public class WeakHashMap extends AbstractMap implements Map
      * Creates a new bucket for the given key/value pair and the specified
      * slot.
      * @param key the key
+     * @param queue the queue the weak reference belongs to
      * @param value the value
      * @param slot the slot.  This must match the slot where this bucket
-     * will be enqueued.
+     *        will be enqueued.
      */
     public WeakBucket(Object key, ReferenceQueue queue, Object value,
-                     int slot)
+                      int slot)
     {
       super(key, queue);
       this.value = value;
@@ -342,11 +374,11 @@ public class WeakHashMap extends AbstractMap implements Map
     }
 
     /**
-     * This class gives the <code>Entry</code> representation of the 
+     * This class gives the <code>Entry</code> representation of the
      * current bucket.  It also keeps a strong reference to the
      * key; bad things may happen otherwise.
      */
-    class Entry implements Map.Entry
+    class WeakEntry implements Map.Entry
     {
       /**
        * The strong ref to the key.
@@ -355,93 +387,105 @@ public class WeakHashMap extends AbstractMap implements Map
 
       /**
        * Creates a new entry for the key.
+       * @param key the key
        */
-      public Entry(Object key)
+      public WeakEntry(Object key)
       {
-       this.key = key;
+        this.key = key;
       }
 
       /**
        * Returns the underlying bucket.
+       * @return the owning bucket
        */
       public WeakBucket getBucket()
       {
-       return WeakBucket.this;
+        return WeakBucket.this;
       }
 
       /**
        * Returns the key.
+       * @return the key
        */
       public Object getKey()
       {
-       return key == NULL_KEY ? null : key;
+        return key == NULL_KEY ? null : key;
       }
 
       /**
        * Returns the value.
+       * @return the value
        */
       public Object getValue()
       {
-       return value;
+        return value;
       }
 
       /**
-       * This changes the value.  This change takes place in 
+       * This changes the value.  This change takes place in
        * the underlying hash map.
+       * @param newVal the new value
+       * @return the old value
        */
       public Object setValue(Object newVal)
       {
-       Object oldVal = value;
-       value = newVal;
-       return oldVal;
+        Object oldVal = value;
+        value = newVal;
+        return oldVal;
       }
 
       /**
        * The hashCode as specified in the Entry interface.
+       * @return the hash code
        */
       public int hashCode()
       {
-       return (key == NULL_KEY ? 0 : key.hashCode())
-         ^ (value == null ? 0 : value.hashCode());
+        return key.hashCode() ^ WeakHashMap.hashCode(value);
       }
 
       /**
        * The equals method as specified in the Entry interface.
+       * @param o the object to compare to
+       * @return true iff o represents the same key/value pair
        */
       public boolean equals(Object o)
       {
-       if (o instanceof Map.Entry)
-         {
-           Map.Entry e = (Map.Entry) o;
-           return (key == NULL_KEY
-                   ? e.getKey() == null : key.equals(e.getKey()))
-             && (value == null
-                 ? e.getValue() == null : value.equals(e.getValue()));
-         }
-       return false;
+        if (o instanceof Map.Entry)
+          {
+            Map.Entry e = (Map.Entry) o;
+            return key.equals(e.getKey())
+              && WeakHashMap.equals(value, e.getValue());
+          }
+        return false;
+      }
+
+      public String toString()
+      {
+        return key + "=" + value;
       }
     }
 
     /**
      * This returns the entry stored in this bucket, or null, if the
      * bucket got cleared in the mean time.
+     * @return the Entry for this bucket, if it exists
      */
-    Entry getEntry()
+    WeakEntry getEntry()
     {
-      final Object key = this.get();
+      final Object key = get();
       if (key == null)
-       return null;
-      return new Entry(key);
+        return null;
+      return new WeakEntry(key);
     }
   }
 
   /**
    * The entry set returned by <code>entrySet()</code>.
    */
-  private WeakEntrySet theEntrySet;
+  private final WeakEntrySet theEntrySet;
 
   /**
-   * The hash buckets.  This are linked lists.
+   * The hash buckets.  These are linked lists.
    */
   private WeakBucket[] buckets;
 
@@ -457,7 +501,8 @@ public class WeakHashMap extends AbstractMap implements Map
   /**
    * Creates a new weak hash map with default load factor and the given
    * capacity.
-   * @param initialCapacity the initial capacity 
+   * @param initialCapacity the initial capacity
+   * @throws IllegalArgumentException if initialCapacity is negative
    */
   public WeakHashMap(int initialCapacity)
   {
@@ -466,13 +511,16 @@ public class WeakHashMap extends AbstractMap implements Map
 
   /**
    * Creates a new weak hash map with the given initial capacity and
-   * load factor.  
+   * load factor.
    * @param initialCapacity the initial capacity.
    * @param loadFactor the load factor (see class description of HashMap).
+   * @throws IllegalArgumentException if initialCapacity is negative, or
+   *         loadFactor is non-positive
    */
   public WeakHashMap(int initialCapacity, float loadFactor)
   {
-    if (initialCapacity < 0 || loadFactor <= 0 || loadFactor > 1)
+    // Check loadFactor for NaN as well.
+    if (initialCapacity < 0 || ! (loadFactor > 0))
       throw new IllegalArgumentException();
     this.loadFactor = loadFactor;
     threshold = (int) (initialCapacity * loadFactor);
@@ -481,8 +529,24 @@ public class WeakHashMap extends AbstractMap implements Map
     buckets = new WeakBucket[initialCapacity];
   }
 
-  /** 
-   * simply hashes a non-null Object to its array index 
+  /**
+   * Construct a new WeakHashMap with the same mappings as the given map.
+   * The WeakHashMap has a default load factor of 0.75.
+   *
+   * @param m the map to copy
+   * @throws NullPointerException if m is null
+   * @since 1.3
+   */
+  public WeakHashMap(Map m)
+  {
+    this(m.size(), DEFAULT_LOAD_FACTOR);
+    putAll(m);
+  }
+
+  /**
+   * Simply hashes a non-null Object to its array index.
+   * @param key the key to hash
+   * @return its slot number
    */
   private int hash(Object key)
   {
@@ -498,68 +562,68 @@ public class WeakHashMap extends AbstractMap implements Map
    * Currently the iterator maintains a strong reference to the key, so
    * that is no problem.
    */
-  private void cleanQueue()
+  // Package visible for use by nested classes.
+  void cleanQueue()
   {
     Object bucket = queue.poll();
     while (bucket != null)
       {
-       internalRemove((WeakBucket) bucket);
-       bucket = queue.poll();
+        internalRemove((WeakBucket) bucket);
+        bucket = queue.poll();
       }
   }
 
   /**
    * Rehashes this hashtable.  This will be called by the
-   * <code>add()</code> method if the size grows beyond the threshold.  
+   * <code>add()</code> method if the size grows beyond the threshold.
    * It will grow the bucket size at least by factor two and allocates
    * new buckets.
    */
   private void rehash()
   {
     WeakBucket[] oldBuckets = buckets;
-    int newsize = buckets.length * 2 + 1;      // XXX should be prime.
+    int newsize = buckets.length * 2 + 1; // XXX should be prime.
     threshold = (int) (newsize * loadFactor);
     buckets = new WeakBucket[newsize];
 
-    /* Now we have to insert the buckets again.
-     */
+    // Now we have to insert the buckets again.
     for (int i = 0; i < oldBuckets.length; i++)
       {
-       WeakBucket bucket = oldBuckets[i];
-       WeakBucket nextBucket;
-       while (bucket != null)
-         {
-           nextBucket = bucket.next;
-
-           Object key = bucket.get();
-           if (key == null)
-             {
-               /* This bucket should be removed; it is probably
-                * already on the reference queue.  We don't insert it
-                * at all, and mark it as cleared.  */
-               bucket.slot = -1;
-               size--;
-             }
-           else
-             {
-               /* add this bucket to its new slot */
-               int slot = hash(key);
-               bucket.slot = slot;
-               bucket.next = buckets[slot];
-               buckets[slot] = bucket;
-             }
-           bucket = nextBucket;
-         }
+        WeakBucket bucket = oldBuckets[i];
+        WeakBucket nextBucket;
+        while (bucket != null)
+          {
+            nextBucket = bucket.next;
+
+            Object key = bucket.get();
+            if (key == null)
+              {
+                // This bucket should be removed; it is probably
+                // already on the reference queue.  We don't insert it
+                // at all, and mark it as cleared.
+                bucket.slot = -1;
+                size--;
+              }
+            else
+              {
+                // Add this bucket to its new slot.
+                int slot = hash(key);
+                bucket.slot = slot;
+                bucket.next = buckets[slot];
+                buckets[slot] = bucket;
+              }
+            bucket = nextBucket;
+          }
       }
   }
 
   /**
    * Finds the entry corresponding to key.  Since it returns an Entry
    * it will also prevent the key from being removed under us.
-   * @param key the key. It may be null.
-   * @return The WeakBucket.Entry or null, if the key wasn't found.
+   * @param key the key, may be null
+   * @return The WeakBucket.WeakEntry or null, if the key wasn't found.
    */
-  private WeakBucket.Entry internalGet(Object key)
+  private WeakBucket.WeakEntry internalGet(Object key)
   {
     if (key == null)
       key = NULL_KEY;
@@ -567,11 +631,11 @@ public class WeakHashMap extends AbstractMap implements Map
     WeakBucket bucket = buckets[slot];
     while (bucket != null)
       {
-       WeakBucket.Entry entry = bucket.getEntry();
-       if (entry != null && key.equals(entry.key))
-         return entry;
+        WeakBucket.WeakEntry entry = bucket.getEntry();
+        if (entry != null && key.equals(entry.key))
+          return entry;
 
-       bucket = bucket.next;
+        bucket = bucket.next;
       }
     return null;
   }
@@ -595,41 +659,40 @@ public class WeakHashMap extends AbstractMap implements Map
   /**
    * Removes a bucket from this hash map, if it wasn't removed before
    * (e.g. one time through rehashing and one time through reference queue)
-   * @param bucket the bucket to remove.  
+   * @param bucket the bucket to remove.
    */
   private void internalRemove(WeakBucket bucket)
   {
     int slot = bucket.slot;
     if (slot == -1)
-      /* this bucket was already removed. */
+      // This bucket was already removed.
       return;
 
-    /* mark the bucket as removed.  This is necessary, since the
-     * bucket may be enqueued later by the garbage collection and
-     * internalRemove, will be called a second time.  
-     */
+    // Mark the bucket as removed.  This is necessary, since the
+    // bucket may be enqueued later by the garbage collection, and
+    // internalRemove will be called a second time.
     bucket.slot = -1;
     if (buckets[slot] == bucket)
       buckets[slot] = bucket.next;
     else
       {
-       WeakBucket prev = buckets[slot];
-       /* This may throw a NullPointerException.  It shouldn't but if
-        * a race condition occurred (two threads removing the same
-        * bucket at the same time) it may happen.  <br>
-        * But with race condition many much worse things may happen
-        * anyway.
-        */
-       while (prev.next != bucket)
-         prev = prev.next;
-       prev.next = bucket.next;
+        WeakBucket prev = buckets[slot];
+        /* This may throw a NullPointerException.  It shouldn't but if
+         * a race condition occurred (two threads removing the same
+         * bucket at the same time) it may happen.  <br>
+         * But with race condition many much worse things may happen
+         * anyway.
+         */
+        while (prev.next != bucket)
+          prev = prev.next;
+        prev.next = bucket.next;
       }
     size--;
   }
 
   /**
    * Returns the size of this hash map.  Note that the size() may shrink
-   * spontanously, if the some of the keys were only weakly reachable.
+   * spontaneously, if the some of the keys were only weakly reachable.
    * @return the number of entries in this hash map.
    */
   public int size()
@@ -651,9 +714,10 @@ public class WeakHashMap extends AbstractMap implements Map
 
   /**
    * Tells if the map contains the given key.  Note that the result
-   * may change spontanously, if all the key was only weakly
-   * reachable.  
-   * @return true, iff the map contains an entry for the given key. 
+   * may change spontanously, if the key was only weakly
+   * reachable.
+   * @param key the key to look for
+   * @return true, iff the map contains an entry for the given key.
    */
   public boolean containsKey(Object key)
   {
@@ -662,38 +726,38 @@ public class WeakHashMap extends AbstractMap implements Map
   }
 
   /**
-   * Gets the value the key will be mapped to.
+   * Gets the value the key is mapped to.
    * @return the value the key was mapped to.  It returns null if
-   * the key wasn't in this map, or if the mapped value was explicitly
-   * set to null.  
+   *         the key wasn't in this map, or if the mapped value was
+   *         explicitly set to null.
    */
   public Object get(Object key)
   {
     cleanQueue();
-    WeakBucket.Entry entry = internalGet(key);
+    WeakBucket.WeakEntry entry = internalGet(key);
     return entry == null ? null : entry.getValue();
   }
 
   /**
    * Adds a new key/value mapping to this map.
-   * @param key the key. This may be null.
-   * @param value the value. This may be null.
+   * @param key the key, may be null
+   * @param value the value, may be null
    * @return the value the key was mapped to previously.  It returns
-   * null if the key wasn't in this map, or if the mapped value was
-   * explicitly set to null.  
+   *         null if the key wasn't in this map, or if the mapped value
+   *         was explicitly set to null.
    */
   public Object put(Object key, Object value)
   {
     cleanQueue();
-    WeakBucket.Entry entry = internalGet(key);
+    WeakBucket.WeakEntry entry = internalGet(key);
     if (entry != null)
       return entry.setValue(value);
 
+    modCount++;
     if (size >= threshold)
       rehash();
 
     internalAdd(key, value);
-    modCount++;
     return null;
   }
 
@@ -701,18 +765,18 @@ public class WeakHashMap extends AbstractMap implements Map
    * Removes the key and the corresponding value from this map.
    * @param key the key. This may be null.
    * @return the value the key was mapped to previously.  It returns
-   * null if the key wasn't in this map, or if the mapped value was
-   * explicitly set to null.  */
+   *         null if the key wasn't in this map, or if the mapped value was
+   *         explicitly set to null.
+   */
   public Object remove(Object key)
   {
     cleanQueue();
-    WeakBucket.Entry entry = internalGet(key);
+    WeakBucket.WeakEntry entry = internalGet(key);
     if (entry == null)
-      {
-       return null;
-      }
-    internalRemove(entry.getBucket());
+      return null;
+
     modCount++;
+    internalRemove(entry.getBucket());
     return entry.getValue();
   }
 
@@ -721,11 +785,71 @@ public class WeakHashMap extends AbstractMap implements Map
    * set will not have strong references to the keys, so they can be
    * silently removed.  The returned set has therefore the same
    * strange behaviour (shrinking size(), disappearing entries) as
-   * this weak hash map.  
-   * @return a set representation of the entries.  */
+   * this weak hash map.
+   * @return a set representation of the entries.
+   */
   public Set entrySet()
   {
     cleanQueue();
     return theEntrySet;
   }
+
+  /**
+   * Clears all entries from this map.
+   */
+  public void clear()
+  {
+    super.clear();
+  }
+
+  /**
+   * Returns true if the map contains at least one key which points to
+   * the specified object as a value.  Note that the result
+   * may change spontanously, if its key was only weakly reachable.
+   * @param value the value to search for
+   * @return true if it is found in the set.
+   */
+  public boolean containsValue(Object value)
+  {
+    cleanQueue();
+    return super.containsValue(value);
+  }
+
+  /**
+   * Returns a set representation of the keys in this map.  This
+   * set will not have strong references to the keys, so they can be
+   * silently removed.  The returned set has therefore the same
+   * strange behaviour (shrinking size(), disappearing entries) as
+   * this weak hash map.
+   * @return a set representation of the keys.
+   */
+  public Set keySet()
+  {
+    cleanQueue();
+    return super.keySet();
+  }
+
+  /**
+   * Puts all of the mappings from the given map into this one. If the
+   * key already exists in this map, its value is replaced.
+   * @param m the map to copy in
+   */
+  public void putAll(Map m)
+  {
+    super.putAll(m);
+  }
+
+  /**
+   * Returns a collection representation of the values in this map.  This
+   * collection will not have strong references to the keys, so mappings
+   * can be silently removed.  The returned collection has therefore the same
+   * strange behaviour (shrinking size(), disappearing entries) as
+   * this weak hash map.
+   * @return a collection representation of the values.
+   */
+  public Collection values()
+  {
+    cleanQueue();
+    return super.values();
+  }
 }