summaryrefslogtreecommitdiff
path: root/libjava
diff options
context:
space:
mode:
authorBryce McKinlay <bryce@gcc.gnu.org>2001-12-15 07:47:03 +0000
committerBryce McKinlay <bryce@gcc.gnu.org>2001-12-15 07:47:03 +0000
commitd9fd7154ec7908eff8bbbce75651eccf51064ac1 (patch)
treea0210bc88649e7cd6d847884e12a68146f35d955 /libjava
parentdef9790d51a51a78a700567bb677225a90bc854e (diff)
downloadgcc-d9fd7154ec7908eff8bbbce75651eccf51064ac1.tar.gz
Collections drop from Classpath:
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
Diffstat (limited to 'libjava')
-rw-r--r--libjava/ChangeLog232
-rw-r--r--libjava/Makefile.am1
-rw-r--r--libjava/Makefile.in61
-rw-r--r--libjava/java/util/AbstractCollection.java342
-rw-r--r--libjava/java/util/AbstractList.java809
-rw-r--r--libjava/java/util/AbstractMap.java559
-rw-r--r--libjava/java/util/AbstractSequentialList.java176
-rw-r--r--libjava/java/util/AbstractSet.java72
-rw-r--r--libjava/java/util/ArrayList.java590
-rw-r--r--libjava/java/util/Arrays.java2913
-rw-r--r--libjava/java/util/BasicMapEntry.java95
-rw-r--r--libjava/java/util/BitSet.java569
-rw-r--r--libjava/java/util/Collections.java3715
-rw-r--r--libjava/java/util/Dictionary.java75
-rw-r--r--libjava/java/util/HashMap.java525
-rw-r--r--libjava/java/util/HashSet.java212
-rw-r--r--libjava/java/util/Hashtable.java512
-rw-r--r--libjava/java/util/IdentityHashMap.java1043
-rw-r--r--libjava/java/util/LinkedHashMap.java34
-rw-r--r--libjava/java/util/LinkedHashSet.java149
-rw-r--r--libjava/java/util/LinkedList.java690
-rw-r--r--libjava/java/util/List.java4
-rw-r--r--libjava/java/util/Stack.java58
-rw-r--r--libjava/java/util/TreeMap.java2349
-rw-r--r--libjava/java/util/TreeSet.java333
-rw-r--r--libjava/java/util/Vector.java824
-rw-r--r--libjava/java/util/WeakHashMap.java690
27 files changed, 11473 insertions, 6159 deletions
diff --git a/libjava/ChangeLog b/libjava/ChangeLog
index 40781b01768..3a3294cc366 100644
--- a/libjava/ChangeLog
+++ b/libjava/ChangeLog
@@ -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.
diff --git a/libjava/Makefile.am b/libjava/Makefile.am
index 551be461cb1..277995b3704 100644
--- a/libjava/Makefile.am
+++ b/libjava/Makefile.am
@@ -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 \
diff --git a/libjava/Makefile.in b/libjava/Makefile.in
index 60d003908f2..57471773dd3 100644
--- a/libjava/Makefile.in
+++ b/libjava/Makefile.in
@@ -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 \
diff --git a/libjava/java/util/AbstractCollection.java b/libjava/java/util/AbstractCollection.java
index 1bb73d7d8b4..095e1c41880 100644
--- a/libjava/java/util/AbstractCollection.java
+++ b/libjava/java/util/AbstractCollection.java
@@ -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();
+ }
}
diff --git a/libjava/java/util/AbstractList.java b/libjava/java/util/AbstractList.java
index ba589e335e7..714f92a7bc4 100644
--- a/libjava/java/util/AbstractList.java
+++ b/libjava/java/util/AbstractList.java
@@ -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
diff --git a/libjava/java/util/AbstractMap.java b/libjava/java/util/AbstractMap.java
index e28ce919beb..f8cf79f29b0 100644
--- a/libjava/java/util/AbstractMap.java
+++ b/libjava/java/util/AbstractMap.java
@@ -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();
+ }
}
diff --git a/libjava/java/util/AbstractSequentialList.java b/libjava/java/util/AbstractSequentialList.java
index 81481be0466..2267b02e523 100644
--- a/libjava/java/util/AbstractSequentialList.java
+++ b/libjava/java/util/AbstractSequentialList.java
@@ -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;
diff --git a/libjava/java/util/AbstractSet.java b/libjava/java/util/AbstractSet.java
index 6c3f2196190..e45e47ea84d 100644
--- a/libjava/java/util/AbstractSet.java
+++ b/libjava/java/util/AbstractSet.java
@@ -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,10 +35,28 @@ 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
* true if so. Otherwise, if o is a Set and is the same size as this one, it
@@ -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();
}
+
}
diff --git a/libjava/java/util/ArrayList.java b/libjava/java/util/ArrayList.java
index d6b663414a4..3c75e56672f 100644
--- a/libjava/java/util/ArrayList.java
+++ b/libjava/java/util/ArrayList.java
@@ -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();
}
}
diff --git a/libjava/java/util/Arrays.java b/libjava/java/util/Arrays.java
index 87a40e35547..d52431b7b3f 100644
--- a/libjava/java/util/Arrays.java
+++ b/libjava/java/util/Arrays.java
@@ -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);
- }
- };
-
+
+// 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;
}
+
+// 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;
}
+
+// 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;
}
+
+// 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;
+ }
}
}
diff --git a/libjava/java/util/BasicMapEntry.java b/libjava/java/util/BasicMapEntry.java
index f858cb4ebd9..48fcc146664 100644
--- a/libjava/java/util/BasicMapEntry.java
+++ b/libjava/java/util/BasicMapEntry.java
@@ -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;
diff --git a/libjava/java/util/BitSet.java b/libjava/java/util/BitSet.java
index 1da187558dd..366c0053333 100644
--- a/libjava/java/util/BitSet.java
+++ b/libjava/java/util/BitSet.java
@@ -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;
}
diff --git a/libjava/java/util/Collections.java b/libjava/java/util/Collections.java
index 8e77a802476..2f545028998 100644
--- a/libjava/java/util/Collections.java
+++ b/libjava/java/util/Collections.java
@@ -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,10 +36,55 @@ 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.
*/
private Collections()
@@ -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 AbstractSet()
+ 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>
+ */
+ 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)
+ * Note: This code is also used in Arrays (for sort as well as search).
*/
- private static int compare(Object o1, Object o2, Comparator c)
+ static final 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.
- */
- private static int search(List l, Object key, final 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))
{
- throw new NullPointerException();
+ 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
+ {
+ 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)
{
@@ -322,30 +447,81 @@ 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 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 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. 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,11 +947,12 @@ 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.
- */
+ /**
+ * 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;
/**
@@ -549,149 +961,470 @@ 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
- // Iterate backwards over l
- for (int pos = lsize - 1; pos >= 0; --pos)
+ if (sequential)
+ a = l.toArray();
+
+ 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);
}
}
+
/**
* 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
*/
- // It's not serializable because the spec is broken.
- public static Set singleton(final Object o)
+ public static Set singleton(Object o)
{
- return new AbstractSet()
+ 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>
+ */
+ private static final class SingletonSet extends AbstractSet
+ implements Serializable
+ {
+ /**
+ * 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
* precisely that used by Arrays.sort(Object[]), which offers guaranteed
@@ -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.
-
- 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)
+ /**
+ * 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)
{
- return new UnmodifiableSortedSet(s);
+ l.set(i, l.set(j, l.get(i)));
}
- // 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
+ /**
+ * 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)
{
- private Iterator i;
-
- public UnmodifiableIterator(Iterator i)
- {
- this.i = i;
- }
-
- public Object next()
- {
- return i.next();
- }
- public boolean hasNext()
- {
- return i.hasNext();
- }
- public void remove()
- {
- throw new UnsupportedOperationException();
- }
+ return new SynchronizedCollection(c);
}
- private static class UnmodifiableListIterator extends UnmodifiableIterator
- implements ListIterator
+ /**
+ * 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
{
- // This is stored both here and in the superclass, to avoid excessive
- // casting.
- private ListIterator li;
-
- public UnmodifiableListIterator(ListIterator li)
+ /**
+ * 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)
{
- super(li);
- this.li = li;
- }
-
- public boolean hasPrevious()
- {
- return li.hasPrevious();
- }
- public Object previous()
- {
- return li.previous();
- }
- public int nextIndex()
- {
- return li.nextIndex();
- }
- public int previousIndex()
- {
- return li.previousIndex();
- }
- public void add(Object o)
- {
- throw new UnsupportedOperationException();
- }
- public void set(Object o)
- {
- throw new UnsupportedOperationException();
+ this.c = c;
+ mutex = this;
+ if (c == null)
+ throw new NullPointerException();
}
- }
-
- private static class UnmodifiableCollection implements Collection,
- Serializable
- {
- Collection c;
- public UnmodifiableCollection(Collection c)
+ /**
+ * 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)
{
this.c = c;
+ mutex = sync;
}
public boolean add(Object o)
{
- throw new UnsupportedOperationException();
+ synchronized (mutex)
+ {
+ return c.add(o);
+ }
}
- public boolean addAll(Collection c)
+
+ public boolean addAll(Collection col)
{
- throw new UnsupportedOperationException();
+ synchronized (mutex)
+ {
+ return c.addAll(col);
+ }
}
+
public void clear()
{
- throw new UnsupportedOperationException();
+ synchronized (mutex)
+ {
+ c.clear();
+ }
}
+
public boolean contains(Object o)
{
- return c.contains(o);
+ synchronized (mutex)
+ {
+ return c.contains(o);
+ }
}
+
public boolean containsAll(Collection c1)
{
- return c.containsAll(c1);
+ synchronized (mutex)
+ {
+ return c.containsAll(c1);
+ }
}
+
public boolean isEmpty()
{
- return c.isEmpty();
+ synchronized (mutex)
+ {
+ return c.isEmpty();
+ }
}
+
public Iterator iterator()
{
- return new UnmodifiableIterator(c.iterator());
+ synchronized (mutex)
+ {
+ return new SynchronizedIterator(mutex, c.iterator());
+ }
}
+
public boolean remove(Object o)
{
- throw new UnsupportedOperationException();
+ synchronized (mutex)
+ {
+ return c.remove(o);
+ }
}
- public boolean removeAll(Collection c)
+
+ public boolean removeAll(Collection col)
{
- throw new UnsupportedOperationException();
+ synchronized (mutex)
+ {
+ return c.removeAll(col);
+ }
}
- public boolean retainAll(Collection c)
+
+ public boolean retainAll(Collection col)
{
- throw new UnsupportedOperationException();
+ synchronized (mutex)
+ {
+ return c.retainAll(col);
+ }
}
+
public int size()
{
- return c.size();
+ synchronized (mutex)
+ {
+ return c.size();
+ }
}
+
public Object[] toArray()
{
- return c.toArray();
+ synchronized (mutex)
+ {
+ return c.toArray();
+ }
}
- public Object[] toArray(Object[]a)
+
+ public Object[] toArray(Object[] a)
{
- return c.toArray(a);
+ synchronized (mutex)
+ {
+ return c.toArray(a);
+ }
}
+
public String toString()
{
- return c.toString();
+ synchronized (mutex)
+ {
+ return c.toString();
+ }
+ }
+ } // 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)
+ {
+ this.i = i;
+ mutex = sync;
+ }
+
+ public Object next()
+ {
+ synchronized (mutex)
+ {
+ return i.next();
+ }
}
+
+ public boolean hasNext()
+ {
+ synchronized (mutex)
+ {
+ return i.hasNext();
+ }
+ }
+
+ public void remove()
+ {
+ 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()
+ /**
+ * 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.comparator();
+ super(sync, s);
}
- public Object firstKey()
+
+ public boolean equals(Object o)
{
- return sm.firstKey();
+ synchronized (mutex)
+ {
+ return c.equals(o);
+ }
}
- public Object lastKey()
+
+ public int hashCode()
{
- return sm.lastKey();
+ synchronized (mutex)
+ {
+ return c.hashCode();
+ }
}
- public SortedMap headMap(Object toKey)
+ } // 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)
{
- return new UnmodifiableSortedMap(sm.headMap(toKey));
+ super(sm);
+ this.sm = sm;
}
- public SortedMap tailMap(Object fromKey)
+
+ /**
+ * 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.tailMap(fromKey));
+ super(sync, sm);
+ this.sm = sm;
}
- public SortedMap subMap(Object fromKey, Object toKey)
+
+ public Comparator comparator()
{
- return new UnmodifiableSortedMap(sm.subMap(fromKey, toKey));
+ synchronized (mutex)
+ {
+ return sm.comparator();
+ }
}
- }
-
- // 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.
- 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 SynchronizedList(List l)
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ } // 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)
+ /**
+ * 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)
{
- super(sync, s);
- }
- public SynchronizedSet(Set s)
- {
- super(s);
- }
-
- public boolean equals(Object o)
- {
- synchronized(sync)
- {
- return c.equals(o);
- }
- }
- public int hashCode()
- {
- 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)
+ {
+ super(s);
+ }
+
+ // The iterator must return unmodifiable map entries.
+ public Iterator iterator()
{
- return new SynchronizedSet(sync, m.entrySet())
+ 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 int hashCode()
+ {
+ return c.hashCode();
}
- public SynchronizedSortedMap(SortedMap sm)
+ } // 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
diff --git a/libjava/java/util/Dictionary.java b/libjava/java/util/Dictionary.java
index 7530e083351..ef727c71c8b 100644
--- a/libjava/java/util/Dictionary.java
+++ b/libjava/java/util/Dictionary.java
@@ -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();
}
diff --git a/libjava/java/util/HashMap.java b/libjava/java/util/HashMap.java
index 3b351058a95..dcf7e7e340b 100644
--- a/libjava/java/util/HashMap.java
+++ b/libjava/java/util/HashMap.java
@@ -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,7 +299,7 @@ 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;
}
@@ -290,30 +307,6 @@ public class HashMap extends AbstractMap
}
/**
- * 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>
* this key. NOTE: Since the prior value could also be null, you must
@@ -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;
}
/**
@@ -638,6 +653,52 @@ public class HashMap extends AbstractMap
}
/**
+ * 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
* size() > threshold. Note that the existing Entry objects are reused in
@@ -682,35 +743,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.
*
* @param s the stream to write to
@@ -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++;
}
}
}
diff --git a/libjava/java/util/HashSet.java b/libjava/java/util/HashSet.java
index 2b5c31dc7fc..9d9c3315263 100644
--- a/libjava/java/util/HashSet.java
+++ b/libjava/java/util/HashSet.java
@@ -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(), "");
}
}
diff --git a/libjava/java/util/Hashtable.java b/libjava/java/util/Hashtable.java
index 2a90244a4d2..01332698c6f 100644
--- a/libjava/java/util/Hashtable.java
+++ b/libjava/java/util/Hashtable.java
@@ -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,23 +812,25 @@ 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;
}
@@ -806,6 +838,30 @@ public class Hashtable extends Dictionary
}
/**
+ * 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
* size() > threshold. Note that the existing Entry objects are reused in
@@ -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
}
diff --git a/libjava/java/util/IdentityHashMap.java b/libjava/java/util/IdentityHashMap.java
index da028ed9c02..d6a2f7c7c2d 100644
--- a/libjava/java/util/IdentityHashMap.java
+++ b/libjava/java/util/IdentityHashMap.java
@@ -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 ();
}
diff --git a/libjava/java/util/LinkedHashMap.java b/libjava/java/util/LinkedHashMap.java
index 8950d58970b..8503e375ace 100644
--- a/libjava/java/util/LinkedHashMap.java
+++ b/libjava/java/util/LinkedHashMap.java
@@ -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
index 00000000000..f98c32e4b70
--- /dev/null
+++ b/libjava/java/util/LinkedHashSet.java
@@ -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);
+ }
+
+}
diff --git a/libjava/java/util/LinkedList.java b/libjava/java/util/LinkedList.java
index 41ca0936cf2..f2f333c4ccf 100644
--- a/libjava/java/util/LinkedList.java
+++ b/libjava/java/util/LinkedList.java
@@ -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
}
diff --git a/libjava/java/util/List.java b/libjava/java/util/List.java
index f2ee49cd260..a000604de49 100644
--- a/libjava/java/util/List.java
+++ b/libjava/java/util/List.java
@@ -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>.
*
diff --git a/libjava/java/util/Stack.java b/libjava/java/util/Stack.java
index 8647fa48b08..a7b3f5d4e70 100644
--- a/libjava/java/util/Stack.java
+++ b/libjava/java/util/Stack.java
@@ -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;
}
}
diff --git a/libjava/java/util/TreeMap.java b/libjava/java/util/TreeMap.java
index 59d6079e32a..83386d6e54b 100644
--- a/libjava/java/util/TreeMap.java
+++ b/libjava/java/util/TreeMap.java
@@ -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;
+
+ /**
+ * The cache for {@link #entrySet()}.
+ */
+ private transient Set entries;
- /** This TreeMap's comparator, if any. */
- Comparator comparator = null;
+ /**
+ * 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;
- static final long serialVersionUID = 919286545866124006L;
+ /**
+ * 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,684 @@ 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()
+ if (entries == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overriden easily and efficiently.
+ entries = new AbstractSet()
{
- return size;
- }
-
- public Iterator iterator()
- {
- return new TreeIterator(TreeIterator.ENTRIES);
- }
-
- public void clear()
- {
- 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;
- public SortedMap headMap(Object toKey)
- {
- return new SubMap(nil, toKey);
- }
+ // 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.
- public SortedMap tailMap(Object fromKey)
- {
- return new SubMap(fromKey, nil);
- }
+ // Make the root node.
+ root = new Node(null, null, BLACK);
+ size = count;
+ Node row = root;
+ int rowsize;
- /** Returns a "collection view" (or "bag view") of this TreeMap's values. */
- 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()
+ // 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)
{
- return size;
+ Node left = new Node(null, null, RED);
+ left.parent = parent;
+ parent.left = left;
+ parent = parent.right;
+ left.parent.right = nil;
}
-
- public Iterator iterator()
+ // Unlink the remaining nodes of the previous row.
+ while (parent != nil)
{
- return new TreeIterator(TreeIterator.VALUES);
+ Node next = parent.right;
+ parent.right = nil;
+ parent = next;
}
-
- public void clear()
+ }
+
+ /**
+ * 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()
+ {
+ // Exploit fact that nil.left == nil.
+ Node node = root;
+ while (node.left != nil)
+ node = node.left;
+ return node;
+ }
+
+ /**
+ * 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)
+ {
+ Node current = root;
+ while (current != nil)
{
- TreeMap.this.clear();
+ 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 < 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 "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;
@@ -734,24 +965,118 @@ 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 // Exact match.
+ return predecessor(last);
}
- if (comparison <= 0)
- return predecessor(last);
- else
- return 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;
}
- // Find the "lowest" node which is >= key. If key is nil, return first node.
- private Node lowestGreaterThan(Object key)
+ /**
+ * Returns the last sorted node in the map, or nil if empty.
+ *
+ * @return the last node
+ */
+ private Node lastNode()
+ {
+ // Exploit fact that nil.right == nil.
+ Node node = root;
+ while (node.right != nil)
+ node = node.right;
+ return node;
+ }
+
+ /**
+ * 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 firstNode();
+ return first ? firstNode() : nil;
Node last = nil;
Node current = root;
@@ -761,95 +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
- return current;
+ if (comparison > 0)
+ current = current.right;
+ else if (comparison < 0)
+ current = current.left;
+ else
+ return current;
}
- if (comparison > 0)
- return successor(last);
- else
- return last;
- }
+ return comparison > 0 ? successor(last) : last;
+ }
- private void writeObject(ObjectOutputStream out) throws IOException
+ /**
+ * 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)
{
- out.defaultWriteObject();
+ if (node.left != nil)
+ {
+ node = node.left;
+ while (node.right != nil)
+ node = node.right;
+ return node;
+ }
- Node node = firstNode();
- out.writeInt(size);
-
- while (node != nil)
+ Node parent = node.parent;
+ // Exploit fact that nil.left == nil and node is non-nil.
+ while (node == parent.left)
{
- out.writeObject(node.key);
- out.writeObject(node.value);
- node = successor(node);
+ node = parent;
+ parent = node.parent;
}
+ return parent;
}
- private void readObject(ObjectInputStream in)
+ /**
+ * 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
{
- in.defaultReadObject();
- int size = in.readInt();
- putFromObjStream(in, size, true);
- }
+ fabricateTree(count);
+ Node node = firstNode();
- private int compare(Object o1, Object o2)
- {
- if (comparator == null)
- return ((Comparable) o1).compareTo(o2);
- else
- return comparator.compare(o1, o2);
+ while (--count >= 0)
+ {
+ node.key = s.readObject();
+ node.value = readValues ? s.readObject() : "";
+ node = successor(node);
+ }
}
- /* Return the node following Node, or nil if there isn't one. */
- private Node successor(Node node)
+ /**
+ * 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)
{
- if (node.right != nil)
- {
- node = node.right;
- while (node.left != nil)
- node = node.left;
- return node;
- }
+ fabricateTree(count);
+ Node node = firstNode();
- Node parent = node.parent;
- while (parent != nil && node == parent.right)
+ while (--count >= 0)
{
- node = parent;
- parent = parent.parent;
+ node.key = keys.next();
+ node.value = "";
+ node = successor(node);
}
- return parent;
}
- /* Return the node preceeding Node, or nil if there isn't one. */
- private Node predecessor(Node node)
+ /**
+ * 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 (node.left != nil)
+ s.defaultReadObject();
+ int size = s.readInt();
+ putFromObjStream(s, size, true);
+ }
+
+ /**
+ * 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)
+ {
+ 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.left;
- while (node.right != nil)
- node = node.right;
- 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.left)
+ else if (node.right == nil)
{
- node = parent;
- parent = parent.parent;
+ // Node to be deleted has 1 child.
+ splice = node;
+ child = node.left;
}
- return parent;
+ else
+ {
+ // 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;
+ }
+
+ // Unlink splice from the tree.
+ Node parent = splice.parent;
+ if (child != nil)
+ child.parent = parent;
+ if (parent == nil)
+ {
+ // Special case for 0 or 1 node remaining.
+ root = child;
+ return;
+ }
+ 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 Set entrySet()
+ {
+ 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 get(Object key)
+ public Object firstKey()
{
- if (keyInRange(key))
- return TreeMap.this.get(key);
- return null;
+ Node node = lowestGreaterThan(minKey, true);
+ if (node == nil || ! keyInRange(node.key))
+ throw new NoSuchElementException();
+ return node.key;
}
- public Object put(Object key, Object value)
+ public Object get(Object key)
{
if (keyInRange(key))
- return TreeMap.this.put(key, value);
- else
- throw new IllegalArgumentException("Key outside range");
+ return TreeMap.this.get(key);
+ return null;
}
- public Object remove(Object key)
+ public SortedMap headMap(Object toKey)
{
- if (keyInRange(key))
- return TreeMap.this.remove(key);
- else
- return null;
+ if (! keyInRange(toKey))
+ throw new IllegalArgumentException("key outside range");
+ return new SubMap(minKey, toKey);
}
- public int size()
+ public Set keySet()
{
- Node node = lowestGreaterThan(minKey);
- Node max = highestLessThan(maxKey);
+ if (this.keys == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overriden easily and efficiently.
+ this.keys = new AbstractSet()
+ {
+ public int size()
+ {
+ return SubMap.this.size();
+ }
- if (node == nil || max == nil || compare(node.key, max.key) > 0)
- return 0; // Empty.
+ public Iterator iterator()
+ {
+ Node first = lowestGreaterThan(minKey, true);
+ Node max = lowestGreaterThan(maxKey, false);
+ return new TreeIterator(KEYS, first, max);
+ }
- int count = 1;
- while (node != max)
- {
- count++;
- node = successor(node);
- }
+ public void clear()
+ {
+ SubMap.this.clear();
+ }
- return count;
+ 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
diff --git a/libjava/java/util/TreeSet.java b/libjava/java/util/TreeSet.java
index ba852131a13..3d2ef3d24d1 100644
--- a/libjava/java/util/TreeSet.java
+++ b/libjava/java/util/TreeSet.java
@@ -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);
}
}
diff --git a/libjava/java/util/Vector.java b/libjava/java/util/Vector.java
index cef84e51e72..24d80f8ca8f 100644
--- a/libjava/java/util/Vector.java
+++ b/libjava/java/util/Vector.java
@@ -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);
+ }
}
diff --git a/libjava/java/util/WeakHashMap.java b/libjava/java/util/WeakHashMap.java
index 6f39d468108..6366e9822c2 100644
--- a/libjava/java/util/WeakHashMap.java
+++ b/libjava/java/util/WeakHashMap.java
@@ -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();
+ }
}