Commit d9fd7154 by Bryce McKinlay

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
parent def9790d
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> 2001-12-14 Hans Boehm <Hans_Boehm@hp.com>
* libjava/prims.cc: Some old cleanups. The collector now * libjava/prims.cc: Some old cleanups. The collector now
handles test for out of memory. handles test for out of memory.
......
...@@ -1200,6 +1200,7 @@ java/util/IdentityHashMap.java \ ...@@ -1200,6 +1200,7 @@ java/util/IdentityHashMap.java \
java/util/Iterator.java \ java/util/Iterator.java \
java/util/LinkedList.java \ java/util/LinkedList.java \
java/util/LinkedHashMap.java \ java/util/LinkedHashMap.java \
java/util/LinkedHashSet.java \
java/util/List.java \ java/util/List.java \
java/util/ListIterator.java \ java/util/ListIterator.java \
java/util/ListResourceBundle.java \ java/util/ListResourceBundle.java \
......
...@@ -123,19 +123,13 @@ libgcj_basedir = @libgcj_basedir@ ...@@ -123,19 +123,13 @@ libgcj_basedir = @libgcj_basedir@
mkinstalldirs = @mkinstalldirs@ mkinstalldirs = @mkinstalldirs@
AUTOMAKE_OPTIONS = foreign AUTOMAKE_OPTIONS = foreign
@TESTSUBDIR_TRUE@SUBDIRS = \ @TESTSUBDIR_TRUE@SUBDIRS = @TESTSUBDIR_TRUE@$(DIRLTDL) testsuite gcj include
@TESTSUBDIR_TRUE@$(DIRLTDL) testsuite gcj include @TESTSUBDIR_FALSE@SUBDIRS = @TESTSUBDIR_FALSE@$(DIRLTDL) gcj include
@TESTSUBDIR_FALSE@SUBDIRS = \ @USE_LIBDIR_TRUE@toolexeclibdir = @USE_LIBDIR_TRUE@$(libdir)$(MULTISUBDIR)
@TESTSUBDIR_FALSE@$(DIRLTDL) gcj include @USE_LIBDIR_FALSE@toolexeclibdir = @USE_LIBDIR_FALSE@$(toolexecdir)/lib$(MULTISUBDIR)
@USE_LIBDIR_TRUE@toolexeclibdir = \ @USE_LIBDIR_FALSE@toolexecdir = @USE_LIBDIR_FALSE@$(exec_prefix)/$(target_alias)
@USE_LIBDIR_TRUE@$(libdir)$(MULTISUBDIR) @XLIB_AWT_TRUE@cond_x_ltlibrary = @XLIB_AWT_TRUE@libgcjx.la
@USE_LIBDIR_FALSE@toolexeclibdir = \ @XLIB_AWT_FALSE@cond_x_ltlibrary =
@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_LTLIBRARIES = libgcj.la $(cond_x_ltlibrary)
toolexeclib_DATA = libgcj.spec toolexeclib_DATA = libgcj.spec
...@@ -143,20 +137,14 @@ data_DATA = libgcj.jar ...@@ -143,20 +137,14 @@ data_DATA = libgcj.jar
secdir = $(libdir)/security secdir = $(libdir)/security
@NATIVE_TRUE@bin_PROGRAMS = \ @NATIVE_TRUE@bin_PROGRAMS = @NATIVE_TRUE@jv-convert gij rmic rmiregistry
@NATIVE_TRUE@jv-convert gij rmic rmiregistry
bin_SCRIPTS = addr2name.awk bin_SCRIPTS = addr2name.awk
@CANADIAN_TRUE@@NULL_TARGET_TRUE@ZIP = \ @CANADIAN_TRUE@@NULL_TARGET_TRUE@ZIP = @CANADIAN_TRUE@@NULL_TARGET_TRUE@$(MULTIBUILDTOP)../$(COMPPATH)/fastjar/jar$(EXEEXT)
@CANADIAN_TRUE@@NULL_TARGET_TRUE@$(MULTIBUILDTOP)../$(COMPPATH)/fastjar/jar$(EXEEXT) @CANADIAN_TRUE@@NULL_TARGET_FALSE@ZIP = @CANADIAN_TRUE@@NULL_TARGET_FALSE@jar
@CANADIAN_TRUE@@NULL_TARGET_FALSE@ZIP = \ @CANADIAN_FALSE@ZIP = @CANADIAN_FALSE@$(MULTIBUILDTOP)../$(COMPPATH)/fastjar/jar$(EXEEXT)
@CANADIAN_TRUE@@NULL_TARGET_FALSE@jar @CANADIAN_TRUE@GCJH = @CANADIAN_TRUE@gcjh
@CANADIAN_FALSE@ZIP = \ @CANADIAN_FALSE@GCJH = @CANADIAN_FALSE@$(MULTIBUILDTOP)../$(COMPPATH)/gcc/gcjh$(EXEEXT)
@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 GCJ_WITH_FLAGS = $(GCJ) --encoding=UTF-8
...@@ -176,10 +164,8 @@ AM_CXXFLAGS = -fno-rtti -fnon-call-exceptions \ ...@@ -176,10 +164,8 @@ AM_CXXFLAGS = -fno-rtti -fnon-call-exceptions \
@LIBGCJ_CXXFLAGS@ @X_CFLAGS@ $(WARNINGS) -D_GNU_SOURCE \ @LIBGCJ_CXXFLAGS@ @X_CFLAGS@ $(WARNINGS) -D_GNU_SOURCE \
-DPREFIX="\"$(prefix)\"" -DPREFIX="\"$(prefix)\""
@USING_GCC_TRUE@AM_CFLAGS = \ @USING_GCC_TRUE@AM_CFLAGS = @USING_GCC_TRUE@@LIBGCJ_CFLAGS@ $(WARNINGS)
@USING_GCC_TRUE@@LIBGCJ_CFLAGS@ $(WARNINGS) @USING_GCC_FALSE@AM_CFLAGS = @USING_GCC_FALSE@@LIBGCJ_CFLAGS@
@USING_GCC_FALSE@AM_CFLAGS = \
@USING_GCC_FALSE@@LIBGCJ_CFLAGS@
JCFLAGS = -g JCFLAGS = -g
JC1FLAGS = @LIBGCJ_JAVAFLAGS@ $(GCJFLAGS) JC1FLAGS = @LIBGCJ_JAVAFLAGS@ $(GCJFLAGS)
...@@ -252,8 +238,7 @@ extra_headers = java/lang/Object.h java/lang/Class.h ...@@ -252,8 +238,7 @@ extra_headers = java/lang/Object.h java/lang/Class.h
NM = nm NM = nm
@NATIVE_TRUE@@MAINTAINER_MODE_TRUE@noinst_PROGRAMS = \ @NATIVE_TRUE@@MAINTAINER_MODE_TRUE@noinst_PROGRAMS = @NATIVE_TRUE@@MAINTAINER_MODE_TRUE@gen-from-JIS
@NATIVE_TRUE@@MAINTAINER_MODE_TRUE@gen-from-JIS
CONVERT_DIR = gnu/gcj/convert CONVERT_DIR = gnu/gcj/convert
...@@ -950,6 +935,7 @@ java/util/IdentityHashMap.java \ ...@@ -950,6 +935,7 @@ java/util/IdentityHashMap.java \
java/util/Iterator.java \ java/util/Iterator.java \
java/util/LinkedList.java \ java/util/LinkedList.java \
java/util/LinkedHashMap.java \ java/util/LinkedHashMap.java \
java/util/LinkedHashSet.java \
java/util/List.java \ java/util/List.java \
java/util/ListIterator.java \ java/util/ListIterator.java \
java/util/ListResourceBundle.java \ java/util/ListResourceBundle.java \
...@@ -1585,7 +1571,7 @@ libgcj-test.spec.in libgcj.spec.in ...@@ -1585,7 +1571,7 @@ libgcj-test.spec.in libgcj.spec.in
DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
TAR = tar TAR = gtar
GZIP_ENV = --best GZIP_ENV = --best
DIST_SUBDIRS = @DIRLTDL@ testsuite gcj include @DIRLTDL@ gcj include DIST_SUBDIRS = @DIRLTDL@ testsuite gcj include @DIRLTDL@ gcj include
DEP_FILES = .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \ DEP_FILES = .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \
...@@ -2224,10 +2210,11 @@ 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/GregorianCalendar.P .deps/java/util/HashMap.P \
.deps/java/util/HashSet.P .deps/java/util/Hashtable.P \ .deps/java/util/HashSet.P .deps/java/util/Hashtable.P \
.deps/java/util/IdentityHashMap.P .deps/java/util/Iterator.P \ .deps/java/util/IdentityHashMap.P .deps/java/util/Iterator.P \
.deps/java/util/LinkedHashMap.P .deps/java/util/LinkedList.P \ .deps/java/util/LinkedHashMap.P .deps/java/util/LinkedHashSet.P \
.deps/java/util/List.P .deps/java/util/ListIterator.P \ .deps/java/util/LinkedList.P .deps/java/util/List.P \
.deps/java/util/ListResourceBundle.P .deps/java/util/Locale.P \ .deps/java/util/ListIterator.P .deps/java/util/ListResourceBundle.P \
.deps/java/util/Map.P .deps/java/util/MissingResourceException.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/NoSuchElementException.P .deps/java/util/Observable.P \
.deps/java/util/Observer.P .deps/java/util/Properties.P \ .deps/java/util/Observer.P .deps/java/util/Properties.P \
.deps/java/util/PropertyPermission.P \ .deps/java/util/PropertyPermission.P \
...@@ -2735,7 +2722,7 @@ distdir: $(DISTFILES) ...@@ -2735,7 +2722,7 @@ distdir: $(DISTFILES)
@for file in $(DISTFILES); do \ @for file in $(DISTFILES); do \
d=$(srcdir); \ d=$(srcdir); \
if test -d $$d/$$file; then \ if test -d $$d/$$file; then \
cp -pr $$/$$file $(distdir)/$$file; \ cp -pr $$d/$$file $(distdir)/$$file; \
else \ else \
test -f $(distdir)/$$file \ test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
......
/* AbstractCollection.java -- Abstract implementation of most of Collection /* 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. This file is part of GNU Classpath.
...@@ -42,74 +42,116 @@ import java.lang.reflect.Array; ...@@ -42,74 +42,116 @@ import java.lang.reflect.Array;
* backing data structure allows for a more efficient implementation. The * backing data structure allows for a more efficient implementation. The
* precise implementation used by AbstractCollection is documented, so that * precise implementation used by AbstractCollection is documented, so that
* subclasses can tell which methods could be implemented more efficiently. * 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 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 * Return an Iterator over this collection. The iterator must provide the
* hasNext and next methods and should in addition provide remove if the * hasNext and next methods and should in addition provide remove if the
* collection is modifiable. * collection is modifiable.
*
* @return an iterator
*/ */
public abstract Iterator 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(); public abstract int size();
/** /**
* Add an object to the collection. This implementation always throws an * Add an object to the collection (optional operation). This implementation
* UnsupportedOperationException - it should be overridden if the collection * always throws an UnsupportedOperationException - it should be
* is to be modifiable. * 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 * @param o the object to add
* @return true if the add operation caused the Collection to change * @return true if the add operation caused the Collection to change
* @exception UnsupportedOperationException if the add operation is not * @throws UnsupportedOperationException if the add operation is not
* supported on this collection * 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) 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 * Add all the elements of a given collection to this collection (optional
* implementation obtains an Iterator over the given collection and iterates * operation). This implementation obtains an Iterator over the given
* over it, adding each element with the add(Object) method (thus this method * collection and iterates over it, adding each element with the
* will fail with an UnsupportedOperationException if the add method does). * 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 * @param c the collection to add the elements of to this collection
* @return true if the add operation caused the Collection to change * @return true if the add operation caused the Collection to change
* @exception UnsupportedOperationException if the add operation is not * @throws UnsupportedOperationException if the add operation is not
* supported on this collection * 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) public boolean addAll(Collection c)
{ {
Iterator itr = c.iterator(); Iterator itr = c.iterator();
int size = c.size();
boolean modified = false; boolean modified = false;
for (int pos = 0; pos < size; pos++) int pos = c.size();
{ while (--pos >= 0)
modified |= add(itr.next()); modified |= add(itr.next());
}
return modified; return modified;
} }
/** /**
* Remove all elements from the collection. This implementation obtains an * Remove all elements from the collection (optional operation). This
* iterator over the collection and calls next and remove on it repeatedly * implementation obtains an iterator over the collection and calls next
* (thus this method will fail with an UnsupportedOperationException if the * and remove on it repeatedly (thus this method will fail with an
* Iterator's remove method does) until there are no more elements to remove. * 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. * Many implementations will have a faster way of doing this.
* *
* @exception UnsupportedOperationException if the Iterator returned by * @throws UnsupportedOperationException if the Iterator returned by
* iterator does not provide an implementation of remove * iterator does not provide an implementation of remove
* @see Iterator#remove()
*/ */
public void clear() public void clear()
{ {
Iterator itr = iterator(); Iterator itr = iterator();
int size = size(); int pos = size();
for (int pos = 0; pos < size; pos++) while (--pos >= 0)
{ {
itr.next(); itr.next();
itr.remove(); itr.remove();
...@@ -130,12 +172,10 @@ public abstract class AbstractCollection implements Collection ...@@ -130,12 +172,10 @@ public abstract class AbstractCollection implements Collection
public boolean contains(Object o) public boolean contains(Object o)
{ {
Iterator itr = iterator(); Iterator itr = iterator();
int size = size(); int pos = size();
for (int pos = 0; pos < size; pos++) while (--pos >= 0)
{ if (equals(o, itr.next()))
if (o == null ? itr.next() == null : o.equals(itr.next()))
return true; return true;
}
return false; return false;
} }
...@@ -148,16 +188,16 @@ public abstract class AbstractCollection implements Collection ...@@ -148,16 +188,16 @@ public abstract class AbstractCollection implements Collection
* @param c the collection to test against * @param c the collection to test against
* @return true if this collection contains all the elements in the given * @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) public boolean containsAll(Collection c)
{ {
Iterator itr = c.iterator(); Iterator itr = c.iterator();
int size = c.size(); int pos = c.size();
for (int pos = 0; pos < size; pos++) while (--pos >= 0)
{
if (!contains(itr.next())) if (!contains(itr.next()))
return false; return false;
}
return true; return true;
} }
...@@ -166,6 +206,7 @@ public abstract class AbstractCollection implements Collection ...@@ -166,6 +206,7 @@ public abstract class AbstractCollection implements Collection
* size() == 0. * size() == 0.
* *
* @return true if this collection is empty. * @return true if this collection is empty.
* @see #size()
*/ */
public boolean isEmpty() public boolean isEmpty()
{ {
...@@ -173,92 +214,131 @@ public abstract class AbstractCollection implements Collection ...@@ -173,92 +214,131 @@ public abstract class AbstractCollection implements Collection
} }
/** /**
* Remove a single instance of an object from this collection. That is, * Remove a single instance of an object from this collection (optional
* remove one element e such that (o == null ? e == null : o.equals(e)), if * operation). That is, remove one element e such that
* such an element exists. This implementation obtains an iterator over the * <code>(o == null ? e == null : o.equals(e))</code>, if such an element
* collection and iterates over it, testing each element for equality with * exists. This implementation obtains an iterator over the collection
* the given object. If it is equal, it is removed by the iterator's remove * and iterates over it, testing each element for equality with the given
* method (thus this method will fail with an UnsupportedOperationException * object. If it is equal, it is removed by the iterator's remove method
* if the Iterator's remove method does). After the first element has been * (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 * removed, true is returned; if the end of the collection is reached, false
* is returned. * is returned.
* *
* @param o the object to remove from this collection * @param o the object to remove from this collection
* @return true if the remove operation caused the Collection to change, or * @return true if the remove operation caused the Collection to change, or
* equivalently if the collection did contain o. * equivalently if the collection did contain o.
* @exception UnsupportedOperationException if this collection's Iterator * @throws UnsupportedOperationException if this collection's Iterator
* does not support the remove method * does not support the remove method
* @see Iterator#remove()
*/ */
public boolean remove(Object o) public boolean remove(Object o)
{ {
Iterator itr = iterator(); Iterator itr = iterator();
int size = size(); int pos = size();
for (int pos = 0; pos < size; pos++) while (--pos >= 0)
{ if (equals(o, itr.next()))
if (o == null ? itr.next() == null : o.equals(itr.next()))
{ {
itr.remove(); itr.remove();
return true; return true;
} }
}
return false; return false;
} }
/** /**
* Remove from this collection all its elements that are contained in a given * Remove from this collection all its elements that are contained in a given
* collection. This implementation iterates over this collection, and for * collection (optional operation). This implementation iterates over this
* each element tests if it is contained in the given collection. If so, it * collection, and for each element tests if it is contained in the given
* is removed by the Iterator's remove method (thus this method will fail * collection. If so, it is removed by the Iterator's remove method (thus
* with an UnsupportedOperationException if the Iterator's remove method * this method will fail with an UnsupportedOperationException if the
* does). * Iterator's remove method does).
* *
* @param c the collection to remove the elements of * @param c the collection to remove the elements of
* @return true if the remove operation caused the Collection to change * @return true if the remove operation caused the Collection to change
* @exception UnsupportedOperationException if this collection's Iterator * @throws UnsupportedOperationException if this collection's Iterator
* does not support the remove method * does not support the remove method
* @see Iterator#remove()
*/ */
public boolean removeAll(Collection c) 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(); Iterator itr = iterator();
int size = size();
boolean modified = false; boolean modified = false;
for (int pos = 0; pos < size; pos++) int pos = size();
{ while (--pos >= 0)
if (c.contains(itr.next())) if (c.contains(itr.next()))
{ {
itr.remove(); itr.remove();
modified = true; modified = true;
} }
}
return modified; return modified;
} }
/** /**
* Remove from this collection all its elements that are not contained in a * Remove from this collection all its elements that are not contained in a
* given collection. This implementation iterates over this collection, and * given collection (optional operation). This implementation iterates over
* for each element tests if it is contained in the given collection. If not, * this collection, and for each element tests if it is contained in the
* it is removed by the Iterator's remove method (thus this method will fail * given collection. If not, it is removed by the Iterator's remove method
* with an UnsupportedOperationException if the Iterator's remove method * (thus this method will fail with an UnsupportedOperationException if
* does). * the Iterator's remove method does).
* *
* @param c the collection to retain the elements of * @param c the collection to retain the elements of
* @return true if the remove operation caused the Collection to change * @return true if the remove operation caused the Collection to change
* @exception UnsupportedOperationException if this collection's Iterator * @throws UnsupportedOperationException if this collection's Iterator
* does not support the remove method * does not support the remove method
* @see Iterator#remove()
*/ */
public boolean retainAll(Collection c) 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(); Iterator itr = iterator();
int size = size();
boolean modified = false; boolean modified = false;
for (int pos = 0; pos < size; pos++) int pos = size();
{ while (--pos >= 0)
if (!c.contains(itr.next())) if (!c.contains(itr.next()))
{ {
itr.remove(); itr.remove();
modified = true; modified = true;
} }
}
return modified; return modified;
} }
...@@ -266,18 +346,18 @@ public abstract class AbstractCollection implements Collection ...@@ -266,18 +346,18 @@ public abstract class AbstractCollection implements Collection
* Return an array containing the elements of this collection. This * Return an array containing the elements of this collection. This
* implementation creates an Object array of size size() and then iterates * implementation creates an Object array of size size() and then iterates
* over the collection, setting each element of the array from the value * 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 * @return an array containing the elements of this collection
*/ */
public Object[] toArray() public Object[] toArray()
{ {
Iterator itr = iterator(); Iterator itr = iterator();
Object[]a = new Object[size()]; int size = size();
for (int pos = 0; pos < a.length; pos++) Object[] a = new Object[size];
{ for (int pos = 0; pos < size; pos++)
a[pos] = itr.next(); a[pos] = itr.next();
}
return a; return a;
} }
...@@ -293,29 +373,29 @@ public abstract class AbstractCollection implements Collection ...@@ -293,29 +373,29 @@ public abstract class AbstractCollection implements Collection
* obtained over the collection and the elements are placed in the array as * 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 * 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. * 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 * @param a the array to copy into, or of the correct run-time type
* @return the array that was produced * @return the array that was produced
* @exception ClassCastException if the type of the array precludes holding * @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 * one of the elements of the Collection
*/ */
public Object[] toArray(Object[]a) public Object[] toArray(Object[] a)
{ {
int size = size(); int size = size();
if (a.length < size) if (a.length < size)
{ a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
a = (Object[])Array.newInstance(a.getClass().getComponentType(),
size); size);
} else if (a.length > size)
a[size] = null;
Iterator itr = iterator(); Iterator itr = iterator();
for (int pos = 0; pos < size; pos++) for (int pos = 0; pos < size; pos++)
{
a[pos] = itr.next(); a[pos] = itr.next();
}
if (a.length > size)
{
a[size] = null;
}
return a; return a;
} }
...@@ -331,15 +411,41 @@ public abstract class AbstractCollection implements Collection ...@@ -331,15 +411,41 @@ public abstract class AbstractCollection implements Collection
public String toString() public String toString()
{ {
Iterator itr = iterator(); Iterator itr = iterator();
int size = size();
StringBuffer r = new StringBuffer("["); StringBuffer r = new StringBuffer("[");
for (int pos = 0; pos < size; pos++) for (int pos = size(); pos > 0; pos--)
{ {
r.append(itr.next()); r.append(itr.next());
if (pos < size - 1) if (pos > 1)
r.append(", "); r.append(", ");
} }
r.append("]"); r.append("]");
return r.toString(); 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();
}
} }
/* AbstractList.java -- Abstract implementation of most of List /* 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. This file is part of GNU Classpath.
...@@ -25,67 +25,192 @@ This exception does not however invalidate any other reasons why the ...@@ -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. */ executable file might be covered by the GNU General Public License. */
// TO DO:
// ~ Doc comments for almost everything.
// ~ Better general commenting
package java.util; package java.util;
/** /**
* A basic implementation of most of the methods in the List interface to make * 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 * it easier to create a List based on a random-access data structure. If
* create an unmodifiable list, it is only necessary to override the size() and * the list is sequential (such as a linked list), use AbstractSequentialList.
* get(int) methods (this contrasts with all other abstract collection classes * To create an unmodifiable list, it is only necessary to override the
* which require an iterator to be provided). To make the list modifiable, the * size() and get(int) methods (this contrasts with all other abstract
* set(int, Object) method should also be overridden, and to make the list * collection classes which require an iterator to be provided). To make the
* resizable, the add(int, Object) and remove(int) methods should be overridden * list modifiable, the set(int, Object) method should also be overridden, and
* too. Other methods should be overridden if the backing data structure allows * to make the list resizable, the add(int, Object) and remove(int) methods
* for a more efficient implementation. The precise implementation used by * should be overridden too. Other methods should be overridden if the
* AbstractList is documented, so that subclasses can tell which methods could * backing data structure allows for a more efficient implementation.
* be implemented more efficiently. * 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 public abstract class AbstractList extends AbstractCollection implements List
{ {
/** /**
* A count of the number of structural modifications that have been made to * 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); 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) public void add(int index, Object o)
{ {
throw new UnsupportedOperationException(); 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) public boolean add(Object o)
{ {
add(size(), o); add(size(), o);
return true; 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) public boolean addAll(int index, Collection c)
{ {
Iterator itr = c.iterator(); Iterator itr = c.iterator();
int size = c.size(); int size = c.size();
for (int pos = 0; pos < size; pos++) for (int pos = size; pos > 0; pos--)
{
add(index++, itr.next()); add(index++, itr.next());
} return size > 0;
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() public void clear()
{ {
removeRange(0, size()); 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) public boolean equals(Object o)
{ {
if (o == this) if (o == this)
return true; return true;
if (!(o instanceof List)) if (! (o instanceof List))
return false; return false;
int size = size(); int size = size();
if (size != ((List) o).size()) if (size != ((List) o).size())
...@@ -94,134 +219,179 @@ public abstract class AbstractList extends AbstractCollection implements List ...@@ -94,134 +219,179 @@ public abstract class AbstractList extends AbstractCollection implements List
Iterator itr1 = iterator(); Iterator itr1 = iterator();
Iterator itr2 = ((List) o).iterator(); Iterator itr2 = ((List) o).iterator();
for (int pos = 0; pos < size; pos++) while (--size >= 0)
{ if (! equals(itr1.next(), itr2.next()))
Object e = itr1.next();
if (e == null ? itr2.next() != null : !e.equals(itr2.next()))
return false; return false;
}
return true; 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() public int hashCode()
{ {
int hashCode = 1; int hashCode = 1;
Iterator itr = iterator(); Iterator itr = iterator();
int size = size(); int pos = size();
for (int pos = 0; pos < size; pos++) while (--pos >= 0)
{ hashCode = 31 * hashCode + hashCode(itr.next());
Object obj = itr.next();
hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
}
return hashCode; 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) public int indexOf(Object o)
{ {
ListIterator itr = listIterator(0); ListIterator itr = listIterator();
int size = size(); int size = size();
for (int pos = 0; pos < size; pos++) for (int pos = 0; pos < size; pos++)
{ if (equals(o, itr.next()))
if (o == null ? itr.next() == null : o.equals(itr.next()))
return pos; return pos;
}
return -1; 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() public Iterator iterator()
{ {
return new AbstractListItr(0); // Bah, Sun's implementation forbids using listIterator(0).
} return new Iterator()
public int lastIndexOf(Object o)
{ {
int size = size(); private int pos = 0;
ListIterator itr = listIterator(size); private int size = size();
for (int pos = size; pos > 0; pos--) private int last = -1;
private int knownMod = modCount;
// This will get inlined, since it is private.
private void checkMod()
{ {
if (o == null ? itr.previous() == null : o.equals(itr.previous())) if (knownMod != modCount)
return pos - 1; throw new ConcurrentModificationException();
}
return -1;
} }
/** public boolean hasNext()
* Return an Iterator over this List. This implementation calls
* listIterator(0).
*
* @return an Iterator over this List
*/
public ListIterator listIterator()
{ {
return listIterator(0); checkMod();
return pos < size;
} }
public ListIterator listIterator(int index) public Object next()
{ {
if (index < 0 || index > size()) checkMod();
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + if (pos == size)
size()); throw new NoSuchElementException();
last = pos;
return new AbstractListItr(index); return get(pos++);
} }
public Object remove(int index) public void remove()
{ {
throw new UnsupportedOperationException(); checkMod();
if (last < 0)
throw new IllegalStateException();
AbstractList.this.remove(last);
pos--;
size--;
last = -1;
knownMod = modCount;
}
};
} }
/** /**
* Remove a subsection of the list. This is called by the clear and * Obtain the last index at which a given object is to be found in this
* removeRange methods of the class which implements subList, which are * list. This implementation grabs listIterator(size()), then searches
* difficult for subclasses to override directly. Therefore, this method * backwards for a match or returns -1.
* should be overridden instead by the more efficient implementation, if one
* exists.
* <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 -
* fromIndex times.
* *
* @param fromIndex the index, inclusive, to remove from. * @return the greatest integer n such that <code>o == null ? get(n) == null
* @param toIndex the index, exclusive, to remove to. * : o.equals(get(n))</code>, or -1 if there is no such index
*/ */
protected void removeRange(int fromIndex, int toIndex) public int lastIndexOf(Object o)
{
ListIterator itr = listIterator(fromIndex);
for (int index = fromIndex; index < toIndex; index++)
{ {
itr.next(); int pos = size();
itr.remove(); ListIterator itr = listIterator(pos);
} while (--pos >= 0)
if (equals(o, itr.previous()))
return pos;
return -1;
} }
public Object set(int index, Object o) /**
* Obtain a ListIterator over this list, starting at the beginning. This
* implementation returns listIterator(0).
*
* @return a ListIterator over the elements of this list, in order, starting
* at the beginning
*/
public ListIterator listIterator()
{ {
throw new UnsupportedOperationException(); return listIterator(0);
} }
public List subList(int fromIndex, int toIndex) /**
* 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 (fromIndex > toIndex) if (index < 0 || index > size())
throw new IllegalArgumentException(fromIndex + " > " + toIndex); throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
if (fromIndex < 0 || toIndex > size()) + size());
throw new IndexOutOfBoundsException();
return new SubList(this, fromIndex, toIndex);
}
class AbstractListItr implements ListIterator return new ListIterator()
{ {
private int knownMod = modCount; private int knownMod = modCount;
private int position; private int position = index;
private int lastReturned = -1; private int lastReturned = -1;
private int size = size(); private int size = size();
AbstractListItr(int start_pos) // This will get inlined, since it is private.
{
this.position = start_pos;
}
private void checkMod() private void checkMod()
{ {
if (knownMod != modCount) if (knownMod != modCount)
...@@ -243,30 +413,20 @@ public abstract class AbstractList extends AbstractCollection implements List ...@@ -243,30 +413,20 @@ public abstract class AbstractList extends AbstractCollection implements List
public Object next() public Object next()
{ {
checkMod(); checkMod();
if (position < size) if (position == size)
{
lastReturned = position++;
return get(lastReturned);
}
else
{
throw new NoSuchElementException(); throw new NoSuchElementException();
} lastReturned = position;
return get(position++);
} }
public Object previous() public Object previous()
{ {
checkMod(); checkMod();
if (position > 0) if (position == 0)
{ throw new NoSuchElementException();
lastReturned = --position; lastReturned = --position;
return get(lastReturned); return get(lastReturned);
} }
else
{
throw new NoSuchElementException();
}
}
public int nextIndex() public int nextIndex()
{ {
...@@ -284,13 +444,12 @@ public abstract class AbstractList extends AbstractCollection implements List ...@@ -284,13 +444,12 @@ public abstract class AbstractList extends AbstractCollection implements List
{ {
checkMod(); checkMod();
if (lastReturned < 0) if (lastReturned < 0)
{
throw new IllegalStateException(); throw new IllegalStateException();
}
AbstractList.this.remove(lastReturned); AbstractList.this.remove(lastReturned);
knownMod++; size--;
position = lastReturned; position = lastReturned;
lastReturned = -1; lastReturned = -1;
knownMod = modCount;
} }
public void set(Object o) public void set(Object o)
...@@ -305,37 +464,179 @@ public abstract class AbstractList extends AbstractCollection implements List ...@@ -305,37 +464,179 @@ public abstract class AbstractList extends AbstractCollection implements List
{ {
checkMod(); checkMod();
AbstractList.this.add(position++, o); AbstractList.this.add(position++, o);
size++;
lastReturned = -1; lastReturned = -1;
knownMod++; 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();
}
/**
* Remove a subsection of the list. This is called by the clear and
* 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. 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 -
* fromIndex times.
*
* @param fromIndex the index, inclusive, to remove from.
* @param toIndex the index, exclusive, to remove to.
*/
protected void removeRange(int fromIndex, int toIndex)
{
ListIterator itr = listIterator(fromIndex);
for (int index = fromIndex; index < toIndex; index++)
{
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);
} }
} // 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 class SubList extends AbstractList
{ {
private AbstractList backingList; private final AbstractList backingList;
private int offset; private final int offset;
private int size; 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; backingList = backing;
modCount = backingList.modCount; modCount = backing.modCount;
offset = fromIndex; offset = fromIndex;
size = toIndex - fromIndex; size = toIndex - fromIndex;
} }
/** /**
* This method checks the two modCount fields to ensure that there has * This method checks the two modCount fields to ensure that there has
* not been a concurrent modification. It throws an exception if there * not been a concurrent modification, returning if all is okay.
* has been, and otherwise returns normally.
* Note that since this method is private, it will be inlined.
* *
* @exception ConcurrentModificationException if there has been a * @throws ConcurrentModificationException if the backing list has been
* concurrent modification. * modified externally to this sublist
*/ */
// This will get inlined, since it is private.
private void checkMod() private void checkMod()
{ {
if (modCount != backingList.modCount) if (modCount != backingList.modCount)
...@@ -345,45 +646,64 @@ class SubList extends AbstractList ...@@ -345,45 +646,64 @@ class SubList extends AbstractList
/** /**
* This method checks that a value is between 0 and size (inclusive). If * This method checks that a value is between 0 and size (inclusive). If
* it is not, an exception is thrown. * 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) private void checkBoundsInclusive(int index)
{ {
if (index < 0 || index > size) if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
size); + size);
} }
/** /**
* This method checks that a value is between 0 (inclusive) and size * This method checks that a value is between 0 (inclusive) and size
* (exclusive). If it is not, an exception is thrown. * (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) private void checkBoundsExclusive(int index)
{ {
if (index < 0 || index >= size) if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
size); + size);
} }
/**
* Specified by AbstractList.subList to return the private field size.
*
* @return the sublist size
*/
public int size() public int size()
{ {
checkMod(); checkMod();
return size; 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) public Object set(int index, Object o)
{ {
checkMod(); checkMod();
checkBoundsExclusive(index); checkBoundsExclusive(index);
o = backingList.set(index + offset, o); return backingList.set(index + offset, o);
return 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) public Object get(int index)
{ {
checkMod(); checkMod();
...@@ -391,53 +711,100 @@ class SubList extends AbstractList ...@@ -391,53 +711,100 @@ class SubList extends AbstractList
return backingList.get(index + offset); 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) public void add(int index, Object o)
{ {
checkMod(); checkMod();
checkBoundsInclusive(index); checkBoundsInclusive(index);
backingList.add(index + offset, o); backingList.add(index + offset, o);
this.modCount++;
size++; 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) public Object remove(int index)
{ {
checkMod(); checkMod();
checkBoundsExclusive(index); checkBoundsExclusive(index);
Object o = backingList.remove(index + offset); Object o = backingList.remove(index + offset);
this.modCount++;
size--; size--;
modCount = backingList.modCount;
return o; 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(); checkMod();
checkBoundsExclusive(fromIndex);
checkBoundsInclusive(toIndex);
// this call will catch the toIndex < fromIndex condition
backingList.removeRange(offset + fromIndex, offset + toIndex); backingList.removeRange(offset + fromIndex, offset + toIndex);
this.modCount = backingList.modCount;
size -= toIndex - fromIndex; 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) public boolean addAll(int index, Collection c)
{ {
checkMod(); checkMod();
checkBoundsInclusive(index); checkBoundsInclusive(index);
int csize = c.size(); int csize = c.size();
boolean result = backingList.addAll(offset + index, c); boolean result = backingList.addAll(offset + index, c);
this.modCount = backingList.modCount;
size += csize; size += csize;
modCount = backingList.modCount;
return result; 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() 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) public ListIterator listIterator(final int index)
{ {
checkMod(); checkMod();
...@@ -445,8 +812,8 @@ class SubList extends AbstractList ...@@ -445,8 +812,8 @@ class SubList extends AbstractList
return new ListIterator() return new ListIterator()
{ {
ListIterator i = backingList.listIterator(index + offset); private final ListIterator i = backingList.listIterator(index + offset);
int position = index; private int position = index;
public boolean hasNext() public boolean hasNext()
{ {
...@@ -462,44 +829,36 @@ class SubList extends AbstractList ...@@ -462,44 +829,36 @@ class SubList extends AbstractList
public Object next() public Object next()
{ {
if (position < size) if (position == size)
{
Object o = i.next();
position++;
return o;
}
else
throw new NoSuchElementException(); throw new NoSuchElementException();
position++;
return i.next();
} }
public Object previous() public Object previous()
{ {
if (position > 0) if (position == 0)
{
Object o = i.previous();
position--;
return o;
}
else
throw new NoSuchElementException(); throw new NoSuchElementException();
position--;
return i.previous();
} }
public int nextIndex() public int nextIndex()
{ {
return offset + i.nextIndex(); return i.nextIndex() - offset;
} }
public int previousIndex() public int previousIndex()
{ {
return offset + i.previousIndex(); return i.previousIndex() - offset;
} }
public void remove() public void remove()
{ {
i.remove(); i.remove();
modCount++;
size--; size--;
position = nextIndex(); position = nextIndex();
modCount = backingList.modCount;
} }
public void set(Object o) public void set(Object o)
...@@ -510,14 +869,14 @@ class SubList extends AbstractList ...@@ -510,14 +869,14 @@ class SubList extends AbstractList
public void add(Object o) public void add(Object o)
{ {
i.add(o); i.add(o);
modCount++;
size++; size++;
position++; position++;
modCount = backingList.modCount;
} }
// Here is the reason why the various modCount fields are mostly // Here is the reason why the various modCount fields are mostly
// ignored in this wrapper listIterator. // 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 // Using any other method on this list will call a corresponding
// method on the backing list *after* the backing listIterator // method on the backing list *after* the backing listIterator
// is created, which will in turn cause a ConcurrentModException // is created, which will in turn cause a ConcurrentModException
...@@ -530,9 +889,31 @@ class SubList extends AbstractList ...@@ -530,9 +889,31 @@ class SubList extends AbstractList
// only, but somewhat pointless when the list can be changed under // only, but somewhat pointless when the list can be changed under
// us. // us.
// Either way, no explicit handling of modCount is needed. // Either way, no explicit handling of modCount is needed.
// However modCount++ must be executed in add and remove, and size // However modCount = backingList.modCount must be executed in add
// must also be updated in these two methods, since they do not go // and remove, and size must also be updated in these two methods,
// through the corresponding methods of the subList. // 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
/* AbstractMap.java -- Abstract implementation of most of Map /* 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. This file is part of GNU Classpath.
...@@ -25,22 +25,71 @@ This exception does not however invalidate any other reasons why the ...@@ -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. */ executable file might be covered by the GNU General Public License. */
// TO DO:
// comments
// test suite
package java.util; 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 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;
/** /**
* Remove all entries from this Map. This default implementation calls * The cache for {@link #keySet()}.
* entrySet().clear(). */
// Package visible for use by subclasses.
Set keys;
/**
* The cache for {@link #values()}.
*/
// Package visible for use by subclasses.
Collection values;
/**
* 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 * @throws UnsupportedOperationException if <code>entrySet().clear()</code>
* @specnote The JCL book claims that this implementation always throws * does not support clearing.
* UnsupportedOperationException, while the online docs claim it * @see Set#clear()
* calls entrySet().clear(). We take the later to be correct.
*/ */
public void clear() public void clear()
{ {
...@@ -48,107 +97,174 @@ public abstract class AbstractMap implements Map ...@@ -48,107 +97,174 @@ 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) public boolean containsKey(Object key)
{ {
Object k; Iterator entries = entrySet().iterator();
Set es = entrySet(); int pos = size();
Iterator entries = es.iterator(); while (--pos >= 0)
int size = size(); if (equals(key, ((Map.Entry) entries.next()).getKey()))
for (int pos = 0; pos < size; pos++)
{
k = ((Map.Entry) entries.next()).getKey();
if (key == null ? k == null : key.equals(k))
return true; return true;
}
return false; 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) public boolean containsValue(Object value)
{ {
Object v; Iterator entries = entrySet().iterator();
Set es = entrySet(); int pos = size();
Iterator entries = es.iterator(); while (--pos >= 0)
int size = size(); if (equals(value, ((Map.Entry) entries.next()).getValue()))
for (int pos = 0; pos < size; pos++)
{
v = ((Map.Entry) entries.next()).getValue();
if (value == null ? v == null : value.equals(v))
return true; return true;
}
return false; 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(); 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) public boolean equals(Object o)
{ {
if (o == this) return (o == this ||
return true; (o instanceof Map &&
if (!(o instanceof Map)) entrySet().equals(((Map) o).entrySet())));
return false;
Map m = (Map) o;
Set s = m.entrySet();
Iterator itr = entrySet().iterator();
int size = size();
if (m.size() != size)
return false;
for (int pos = 0; pos < size; pos++)
{
if (!s.contains(itr.next()))
return false;
}
return true;
} }
/**
* 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) public Object get(Object key)
{ {
Set s = entrySet(); Iterator entries = entrySet().iterator();
Iterator entries = s.iterator(); int pos = size();
int size = size(); while (--pos >= 0)
for (int pos = 0; pos < size; pos++)
{ {
Map.Entry entry = (Map.Entry) entries.next(); Map.Entry entry = (Map.Entry) entries.next();
Object k = entry.getKey(); if (equals(key, entry.getKey()))
if (key == null ? k == null : key.equals(k))
return entry.getValue(); return entry.getValue();
} }
return null; 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() public int hashCode()
{ {
int hashcode = 0; return entrySet().hashCode();
Iterator itr = entrySet().iterator();
int size = size();
for (int pos = 0; pos < size; pos++)
{
hashcode += itr.next().hashCode();
}
return 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() public boolean isEmpty()
{ {
return size() == 0; 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() public Set keySet()
{ {
if (this.keySet == null) if (keys == null)
{ keys = new AbstractSet()
this.keySet = new AbstractSet()
{ {
public int size() public int size()
{ {
...@@ -157,14 +273,14 @@ public abstract class AbstractMap implements Map ...@@ -157,14 +273,14 @@ public abstract class AbstractMap implements Map
public boolean contains(Object key) public boolean contains(Object key)
{ {
return AbstractMap.this.containsKey(key); return containsKey(key);
} }
public Iterator iterator() public Iterator iterator()
{ {
return new Iterator() return new Iterator()
{ {
Iterator map_iterator = AbstractMap.this.entrySet().iterator(); private final Iterator map_iterator = entrySet().iterator();
public boolean hasNext() public boolean hasNext()
{ {
...@@ -183,77 +299,156 @@ public abstract class AbstractMap implements Map ...@@ -183,77 +299,156 @@ public abstract class AbstractMap implements Map
}; };
} }
}; };
return keys;
} }
return this.keySet; /**
} * 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) public Object put(Object key, Object value)
{ {
throw new UnsupportedOperationException(); 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) public void putAll(Map m)
{ {
Map.Entry entry;
Iterator entries = m.entrySet().iterator(); Iterator entries = m.entrySet().iterator();
int size = m.size(); int pos = size();
while (--pos >= 0)
for (int pos = 0; pos < size; pos++)
{ {
entry = (Map.Entry) entries.next(); Map.Entry entry = (Map.Entry) entries.next();
put(entry.getKey(), entry.getValue()); 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) public Object remove(Object key)
{ {
Iterator entries = entrySet().iterator(); Iterator entries = entrySet().iterator();
int size = size(); int pos = size();
while (--pos >= 0)
for (int pos = 0; pos < size; pos++)
{ {
Map.Entry entry = (Map.Entry) entries.next(); Map.Entry entry = (Map.Entry) entries.next();
Object k = entry.getKey(); if (equals(key, entry.getKey()))
if (key == null ? k == null : key.equals(k))
{ {
Object value = entry.getValue(); // Must get the value before we remove it from iterator.
Object r = entry.getValue();
entries.remove(); entries.remove();
return value; return r;
} }
} }
return null; 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() public int size()
{ {
return entrySet().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() public String toString()
{ {
Iterator entries = entrySet().iterator(); Iterator entries = entrySet().iterator();
int size = size();
StringBuffer r = new StringBuffer("{"); 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 // Append the toString value of the entries rather than calling
// getKey/getValue. This is more efficient and it matches the JDK // getKey/getValue. This is more efficient and it matches the JDK
// behaviour. // behaviour.
r.append(entries.next()); r.append(entries.next());
if (pos < size - 1) if (pos > 1)
r.append(", "); r.append(", ");
} }
r.append("}"); r.append("}");
return r.toString(); 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() public Collection values()
{ {
if (this.valueCollection == null) if (values == null)
{ values = new AbstractCollection()
this.valueCollection = new AbstractCollection()
{ {
public int size() public int size()
{ {
...@@ -264,7 +459,7 @@ public abstract class AbstractMap implements Map ...@@ -264,7 +459,7 @@ public abstract class AbstractMap implements Map
{ {
return new Iterator() return new Iterator()
{ {
Iterator map_iterator = AbstractMap.this.entrySet().iterator(); private final Iterator map_iterator = entrySet().iterator();
public boolean hasNext() public boolean hasNext()
{ {
...@@ -283,11 +478,33 @@ public abstract class AbstractMap implements Map ...@@ -283,11 +478,33 @@ public abstract class AbstractMap implements Map
}; };
} }
}; };
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();
}
} }
/* AbstractSequentialList.java -- List implementation for sequential access /* 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. This file is part of GNU Classpath.
...@@ -25,100 +25,192 @@ This exception does not however invalidate any other reasons why the ...@@ -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. */ 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; package java.util;
/** /**
* Abstract superclass to make it easier to implement the List interface when * 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 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. * Returns a ListIterator over the list, starting from position index.
* Subclasses must provide an implementation of this method. * 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); public abstract ListIterator listIterator(int index);
/** /**
* Add an element to the list at a given index. This implementation obtains a * Insert an element into the list at a given position (optional operation).
* ListIterator positioned at the specified index, and then adds the element * This shifts all existing elements from that position to the end one
* using the ListIterator's add method. * 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 index the location to insert the item
* @param o the element to insert * @param o the object to insert
* @exception IndexOutOfBoundsException if index < 0 || index > size() * @throws UnsupportedOperationException if this list does not support the
* @exception UnsupportedOperationException if the iterator returned by * add operation
* listIterator(index) does not support the add method. * @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) public void add(int index, Object o)
{ {
ListIterator i = listIterator(index); listIterator(index).add(o);
i.add(o);
} }
/** /**
* @specnote The spec in the JDK1.3 online docs is wrong. The implementation * Insert the contents of a collection into the list at a given position
* should not call next() to skip over new elements as they are * (optional operation). Shift all elements at that position to the right
* added, because iterator.add() should add new elements BEFORE * by the number of elements inserted. This operation is undefined if
* the cursor. * 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) public boolean addAll(int index, Collection c)
{ {
boolean modified = false;
Iterator ci = c.iterator(); Iterator ci = c.iterator();
int size = c.size(); int size = c.size();
ListIterator i = listIterator(index); ListIterator i = listIterator(index);
for (int pos = 0; pos < size; pos++) for (int pos = size; pos > 0; pos--)
{
i.add(ci.next()); i.add(ci.next());
} return size > 0;
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) public Object get(int index)
{ {
ListIterator i = listIterator(index); // This is a legal listIterator position, but an illegal get.
if (index < 0 || index > size()) if (index == size())
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
size()); + size());
return i.next(); return listIterator(index).next();
} }
/** /**
* Return an Iterator over this List. This implementation returns * Obtain an Iterator over this list, whose sequence is the list order. This
* listIterator(). * implementation returns listIterator().
* *
* @return an Iterator over this List * @return an Iterator over the elements of this list, in order
*/ */
public Iterator iterator() public Iterator iterator()
{ {
return listIterator(); 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) 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); ListIterator i = listIterator(index);
if (index < 0 || index > size())
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size());
Object removed = i.next(); Object removed = i.next();
i.remove(); i.remove();
return removed; 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) 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); ListIterator i = listIterator(index);
if (index < 0 || index > size())
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size());
Object old = i.next(); Object old = i.next();
i.set(o); i.set(o);
return old; return old;
......
/* AbstractSet.java -- Abstract implementation of most of Set /* 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. This file is part of GNU Classpath.
...@@ -35,10 +35,28 @@ package java.util; ...@@ -35,10 +35,28 @@ package java.util;
* on them - specifically, no element may be in the set more than once). This * on them - specifically, no element may be in the set more than once). This
* class simply provides implementations of equals() and hashCode() to fulfil * class simply provides implementations of equals() and hashCode() to fulfil
* the requirements placed on them by the Set interface. * 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 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 * 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 * 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 * 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 ...@@ -50,12 +68,9 @@ public abstract class AbstractSet extends AbstractCollection implements Set
*/ */
public boolean equals(Object o) public boolean equals(Object o)
{ {
if (o == this) return (o == this ||
return true; (o instanceof Set && ((Set) o).size() == size()
else if (o instanceof Set && ((Set) o).size() == size()) && containsAll((Collection) o)));
return containsAll((Collection) o);
else
return false;
} }
/** /**
...@@ -69,14 +84,45 @@ public abstract class AbstractSet extends AbstractCollection implements Set ...@@ -69,14 +84,45 @@ public abstract class AbstractSet extends AbstractCollection implements Set
public int hashCode() public int hashCode()
{ {
Iterator itr = iterator(); Iterator itr = iterator();
int size = size();
int hash = 0; 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)
{ {
Object obj = itr.next(); int oldsize = size();
if (obj != null) int count = c.size();
hash += obj.hashCode(); Iterator i;
if (oldsize < count)
{
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();
} }
} }
/* ArrayList.java -- JDK1.2's answer to Vector; this is an array-backed /* ArrayList.java -- JDK1.2's answer to Vector; this is an array-backed
implementation of the List interface 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. This file is part of GNU Classpath.
...@@ -35,39 +35,81 @@ import java.io.ObjectInputStream; ...@@ -35,39 +35,81 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
/** /**
* An array-backed implementation of the List interface. ArrayList * An array-backed implementation of the List interface. This implements
* performs well on simple tasks: random access into a list, appending * all optional list operations, and permits null elements, so that it is
* to or removing from the end of a list, checking the size, &c. * 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>
*
* 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 Jon A. Zeppieri
* @see java.util.AbstractList * @author Bryce McKinlay
* @see java.util.List * @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 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; 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) public ArrayList(int capacity)
{ {
// Must explicitly check, to get correct exception.
if (capacity < 0)
throw new IllegalArgumentException();
data = new Object[capacity]; data = new Object[capacity];
} }
/** /**
* Construct a new ArrayList with the default capcity * Construct a new ArrayList with the default capcity (16).
*/ */
public ArrayList() public ArrayList()
{ {
...@@ -76,112 +118,216 @@ public class ArrayList extends AbstractList ...@@ -76,112 +118,216 @@ public class ArrayList extends AbstractList
/** /**
* Construct a new ArrayList, and initialize it with the elements * Construct a new ArrayList, and initialize it with the elements
* in the supplied Collection; Sun specs say that the initial * in the supplied Collection. The initial capacity is 110% of the
* capacity is 110% of the Collection's size. * Collection's size.
* *
* @param c the collection whose elements will initialize this list * @param c the collection whose elements will initialize this list
* @throws NullPointerException if c is null
*/ */
public ArrayList(Collection c) public ArrayList(Collection c)
{ {
this((int) (c.size() * 1.1)); this((int) (c.size() * 1.1f));
addAll(c); 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 * Guarantees that this list will have at least enough capacity to
* hold minCapacity elements. * hold minCapacity elements. This implementation will grow the list to
*
* @specnote This implementation will grow the list to
* max(current * 2, minCapacity) if (minCapacity > current). The JCL says * max(current * 2, minCapacity) if (minCapacity > current). The JCL says
* explictly that "this method increases its capacity to minCap", while * 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 * the JDK 1.3 online docs specify that the list will grow to at least the
* size specified. * size specified.
*
* @param minCapacity the minimum guaranteed capacity * @param minCapacity the minimum guaranteed capacity
*/ */
public void ensureCapacity(int minCapacity) public void ensureCapacity(int minCapacity)
{ {
Object[] newData;
int current = data.length; int current = data.length;
if (minCapacity > current) if (minCapacity > current)
{ {
newData = new Object[Math.max((current * 2), minCapacity)]; Object[] newData = new Object[Math.max(current * 2, minCapacity)];
System.arraycopy(data, 0, newData, 0, size); System.arraycopy(data, 0, newData, 0, size);
data = newData; 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++; return size;
if (size == data.length)
ensureCapacity(size + 1);
data[size++] = e;
return true;
} }
/** /**
* Retrieves the element at the user-supplied index. * Checks if the list is empty.
* *
* @param index the index of the element we are fetching * @return true if there are no elements
* @throws IndexOutOfBoundsException (iIndex < 0) || (iIndex >= size())
*/ */
public Object get(int index) public boolean isEmpty()
{ {
if (index < 0 || index >= size) return size == 0;
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size);
return data[index];
} }
/** /**
* 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;
}
/**
* Returns the lowest index at which element appears in this List, or
* -1 if it does not appear.
*
* @param e the element whose inclusion in the List is being tested
* @return the index where e was found
*/
public int indexOf(Object e)
{
for (int i = 0; i < size; i++)
if (equals(e, data[i]))
return i;
return -1;
} }
/** /**
* Removes the element at the user-supplied index * Returns the highest 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 * @param e the element whose inclusion in the List is being tested
* @return the removed Object * @return the index where e was found
* @throws IndexOutOfBoundsException (iIndex < 0) || (iIndex >= size())
*/ */
public Object remove(int index) public int lastIndexOf(Object e)
{ {
modCount++; for (int i = size - 1; i >= 0; i--)
if (index < 0 || index > size) if (equals(e, data[i]))
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + 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)
{
// 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); size);
Object r = data[index]; else if (a.length > size)
if (index != --size) a[size] = null;
System.arraycopy(data, (index + 1), data, index, (size - index)); System.arraycopy(data, 0, a, 0, size);
data[size] = null; return a;
return r;
} }
/** /**
* Removes all elements in the half-open interval [iFromIndex, iToIndex). * Retrieves the element at the user-supplied index.
* *
* @param fromIndex the first index which will be removed * @param index the index of the element we are fetching
* @param toIndex one greater than the last index which will be * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
* removed
*/ */
protected void removeRange(int fromIndex, int toIndex) public Object get(int index)
{ {
modCount++; checkBoundExclusive(index);
if (fromIndex != toIndex) 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)
{ {
System.arraycopy(data, toIndex, data, fromIndex, size - toIndex); checkBoundExclusive(index);
size -= (toIndex - fromIndex); 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;
} }
/** /**
...@@ -190,13 +336,12 @@ public class ArrayList extends AbstractList ...@@ -190,13 +336,12 @@ public class ArrayList extends AbstractList
* *
* @param index the index at which the element is being added * @param index the index at which the element is being added
* @param e the item being added * @param e the item being added
* @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
*/ */
public void add(int index, Object e) public void add(int index, Object e)
{ {
checkBoundInclusive(index);
modCount++; modCount++;
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size);
if (size == data.length) if (size == data.length)
ensureCapacity(size + 1); ensureCapacity(size + 1);
if (index != size) if (index != size)
...@@ -206,10 +351,46 @@ public class ArrayList extends AbstractList ...@@ -206,10 +351,46 @@ public class ArrayList extends AbstractList
} }
/** /**
* 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 * @param c a Collection containing elements to be added to this List
* 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) public boolean addAll(Collection c)
{ {
...@@ -221,197 +402,168 @@ public class ArrayList extends AbstractList ...@@ -221,197 +402,168 @@ public class ArrayList extends AbstractList
* at the specified index. * at the specified index.
* *
* @param index the index at which the elements will be inserted * @param index the index at which the elements will be inserted
* @param c the Collection containing the elements to be * @param c the Collection containing the elements to be inserted
* inserted * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; 0
* @throws NullPointerException if c is null
*/ */
public boolean addAll(int index, Collection c) public boolean addAll(int index, Collection c)
{ {
if (index < 0 || index > size) checkBoundInclusive(index);
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size);
modCount++;
Iterator itr = c.iterator(); Iterator itr = c.iterator();
int csize = c.size(); int csize = c.size();
modCount++;
if (csize + size > data.length) if (csize + size > data.length)
ensureCapacity(size + csize); ensureCapacity(size + csize);
int end = index + csize; int end = index + csize;
if (size > 0 && index != size) if (index != size)
System.arraycopy(data, index, data, end, csize); System.arraycopy(data, index, data, end, csize);
size += csize; size += csize;
for (; index < end; index++) for ( ; index < end; index++)
{
data[index] = itr.next(); data[index] = itr.next();
} return csize > 0;
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; if (fromIndex != toIndex)
try
{ {
clone = (ArrayList) super.clone(); modCount++;
clone.data = new Object[data.length]; System.arraycopy(data, toIndex, data, fromIndex, size - toIndex);
System.arraycopy(data, 0, clone.data, 0, size); 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 * @param index the index to check
* tested * @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 * Checks that the index is in the range of existing elements (exclusive).
* -1 if it does not appear.
* *
* @param e the element whose inclusion in the List is being * @param index the index to check
* tested * @throws IndexOutOfBoundsException if index &gt;= size
*/ */
public int indexOf(Object e) private void checkBoundExclusive(int index)
{ {
for (int i = 0; i < size; i++) // Implementation note: we do not check for negative ranges here, since
{ // use of a negative index will cause an ArrayIndexOutOfBoundsException,
if (e == null ? data[i] == null : e.equals(data[i])) // a subclass of the required exception, with no effort on our part.
return i; if (index >= size)
} throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
return -1; + size);
} }
/** /**
* Returns the highest index at which oElement appears in this List, or * Remove from this list all elements contained in the given collection.
* -1 if it does not appear. * 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 * @param c the collection to filter out
* tested * @return true if this list changed
* @throws NullPointerException if c is null
*/ */
public int lastIndexOf(Object e) boolean removeAllInternal(Collection c)
{ {
int i; 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++; modCount++;
for (int i = 0; i < size; i++) for (j = i++; i < size; i++)
{ if (! c.contains(data[i]))
data[i] = null; data[j++] = data[i];
} size -= i - j;
size = 0; 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 c the collection to filter by
* @param e the element to be set * @return true if this vector changed
* @return the element previously at the specified index, or null if * @throws NullPointerException if c is null
* none was there * @since 1.2
*/ */
public Object set(int index, Object e) boolean retainAllInternal(Collection c)
{ {
Object result; int i;
if (index < 0 || index >= size) int j;
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + for (i = 0; i < size; i++)
size); if (! c.contains(data[i]))
result = data[index]; break;
// SEH: no structural change, so don't update modCount if (i == size)
data[index] = e; return false;
return result;
}
/** modCount++;
* Returns an Object Array containing all of the elements in this ArrayList for (j = i++; i < size; i++)
*/ if (c.contains(data[i]))
public Object[] toArray() data[j++] = data[i];
{ size -= i - j;
Object[] array = new Object[size]; return true;
System.arraycopy(data, 0, array, 0, size);
return array;
} }
/** /**
* Returns an Array whose component type is the runtime component type of * Serializes this object to the given stream.
* 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 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) // The 'size' field.
array = (Object[]) Array.newInstance(array.getClass().getComponentType(), s.defaultWriteObject();
size); // We serialize unused list entries to preserve capacity.
else if (array.length > size) int len = data.length;
array[size] = null; s.writeInt(len);
System.arraycopy(data, 0, array, 0, size); for (int i = 0; i < len; i++)
return array; s.writeObject(data[i]);
} }
/** /**
* Trims the capacity of this List to be equal to its size; * Deserializes this object from the given stream.
* a memory saver. *
* @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() private void readObject(ObjectInputStream s)
{
// 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)
throws IOException, ClassNotFoundException throws IOException, ClassNotFoundException
{ {
int i;
int capacity;
// the `size' field. // the `size' field.
in.defaultReadObject(); s.defaultReadObject();
int capacity = s.readInt();
capacity = in.readInt();
data = new Object[capacity]; data = new Object[capacity];
for (int i = 0; i < capacity; i++)
for (i = 0; i < capacity; i++) data[i] = s.readObject();
data[i] = in.readObject();
} }
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
/* BasicMapEntry.java -- a class providing a plain-vanilla implementation of /* BasicMapEntry.java -- a class providing a plain-vanilla implementation of
the Map.Entry interface; could be used anywhere in java.util 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. This file is part of GNU Classpath.
...@@ -29,52 +29,113 @@ executable file might be covered by the GNU General Public License. */ ...@@ -29,52 +29,113 @@ executable file might be covered by the GNU General Public License. */
package java.util; package java.util;
/** /**
* A class which implements Map.Entry. It is shared by HashMap, TreeMap, and * A class which implements Map.Entry. It is shared by HashMap, TreeMap,
* Hashtable. * 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 class BasicMapEntry implements Map.Entry
{ {
/**
* The key. Package visible for direct manipulation.
*/
Object key; Object key;
/**
* The value. Package visible for direct manipulation.
*/
Object value; Object value;
/**
* Basic constructor initializes the fields.
* @param newKey the key
* @param newValue the value
*/
BasicMapEntry(Object newKey, Object newValue) BasicMapEntry(Object newKey, Object newValue)
{ {
key = newKey; key = newKey;
value = newValue; 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) public final boolean equals(Object o)
{ {
if (!(o instanceof Map.Entry)) if (! (o instanceof Map.Entry))
return false; 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; Map.Entry e = (Map.Entry) o;
return (key == null ? e.getKey() == null : key.equals(e.getKey()) return (AbstractCollection.equals(key, e.getKey())
&& value == null ? e.getValue() == null && AbstractCollection.equals(value, e.getValue()));
: value.equals(e.getValue()));
} }
/**
* Get the key corresponding to this entry.
*
* @return the key
*/
public final Object getKey() public final Object getKey()
{ {
return key; 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() public final Object getValue()
{ {
return value; 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() public final int hashCode()
{ {
int kc = (key == null ? 0 : key.hashCode()); return (AbstractCollection.hashCode(key)
int vc = (value == null ? 0 : value.hashCode()); ^ AbstractCollection.hashCode(value));
return kc ^ vc;
} }
/** /**
* sets the value of this Map.Entry. Note that this is overriden by * Replaces the value with the specified object. This writes through
* Hashtable.Entry, which does not permit a null value. * 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) public Object setValue(Object newVal)
{ {
...@@ -83,6 +144,12 @@ class BasicMapEntry implements Map.Entry ...@@ -83,6 +144,12 @@ class BasicMapEntry implements Map.Entry
return r; 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() public final String toString()
{ {
return key + "=" + value; return key + "=" + value;
......
// BitSet - A vector of bits. /* BitSet.java -- A vector of bits.
Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
/* Copyright (C) 1998, 1999, 2000 Free Software Foundation
This file is part of GNU Classpath. This file is part of GNU Classpath.
...@@ -50,22 +49,32 @@ import java.io.Serializable; ...@@ -50,22 +49,32 @@ import java.io.Serializable;
* while another thread is simultaneously modifying it, the results are * while another thread is simultaneously modifying it, the results are
* undefined. * 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 Jochen Hoenicke
* @author Tom Tromey <tromey@cygnus.com> * @author Tom Tromey <tromey@cygnus.com>
* @date October 23, 1998. * @author Eric Blake <ebb9@email.byu.edu>
* @status API complete to JDK 1.3. * @status updated to 1.4
*/ */
public class BitSet implements Cloneable, Serializable 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() public BitSet()
{ {
...@@ -76,17 +85,14 @@ public class BitSet implements Cloneable, Serializable ...@@ -76,17 +85,14 @@ public class BitSet implements Cloneable, Serializable
* Create a new empty bit set, with a given size. This * Create a new empty bit set, with a given size. This
* constructor reserves enough space to represent the integers * constructor reserves enough space to represent the integers
* from <code>0</code> to <code>nbits-1</code>. * from <code>0</code> to <code>nbits-1</code>.
* @param nbits the initial size of the bit set. *
* @throws NegativeArraySizeException if the specified initial * @param nbits the initial size of the bit set
* size is negative. * @throws NegativeArraySizeException if nbits &lt; 0
* @require nbits >= 0
*/ */
public BitSet(int nbits) public BitSet(int nbits)
{ {
if (nbits < 0) int length = nbits >>> 6;
throw new NegativeArraySizeException(); if ((nbits & LONG_MASK) != 0)
int length = nbits / 64;
if (nbits % 64 != 0)
++length; ++length;
bits = new long[length]; bits = new long[length];
} }
...@@ -95,8 +101,9 @@ public class BitSet implements Cloneable, Serializable ...@@ -95,8 +101,9 @@ public class BitSet implements Cloneable, Serializable
* Performs the logical AND operation on this bit set and the * Performs the logical AND operation on this bit set and the
* given <code>set</code>. This means it builds the intersection * given <code>set</code>. This means it builds the intersection
* of the two sets. The result is stored into this bit set. * 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) public void and(BitSet bs)
{ {
...@@ -104,8 +111,8 @@ public class BitSet implements Cloneable, Serializable ...@@ -104,8 +111,8 @@ public class BitSet implements Cloneable, Serializable
int i; int i;
for (i = 0; i < max; ++i) for (i = 0; i < max; ++i)
bits[i] &= bs.bits[i]; bits[i] &= bs.bits[i];
for (; i < bits.length; ++i) while (i < bits.length)
bits[i] = 0; bits[i++] = 0;
} }
/** /**
...@@ -113,54 +120,134 @@ public class BitSet implements Cloneable, Serializable ...@@ -113,54 +120,134 @@ public class BitSet implements Cloneable, Serializable
* complement of the given <code>set</code>. This means it * complement of the given <code>set</code>. This means it
* selects every element in the first set, that isn't in the * selects every element in the first set, that isn't in the
* second set. The result is stored into this bit set. * second set. The result is stored into this bit set.
* @param set the second bit set. *
* @require set != null * @param set the second bit set
* @since JDK1.2 * @throws NullPointerException if set is null
* @since 1.2
*/ */
public void andNot(BitSet bs) public void andNot(BitSet bs)
{ {
int max = Math.min(bits.length, bs.bits.length); int i = Math.min(bits.length, bs.bits.length);
int i; while (--i >= 0)
for (i = 0; i < max; ++i)
bits[i] &= ~bs.bits[i]; 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 * Removes the integer <code>bitIndex</code> from this set. That is
* the corresponding bit is cleared. If the index is not in the set, * the corresponding bit is cleared. If the index is not in the set,
* this method does nothing. * this method does nothing.
* @param bitIndex a non-negative integer. *
* @exception ArrayIndexOutOfBoundsException if the specified bit index * @param bitIndex a non-negative integer
* is negative. * @throws IndexOutOfBoundsException if bitIndex &lt; 0
* @require bitIndex >= 0
*/ */
public void clear(int pos) public void clear(int pos)
{ {
if (pos < 0) int offset = pos >>> 6;
throw new IndexOutOfBoundsException();
int bit = pos % 64;
int offset = pos / 64;
ensure(offset); 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 * 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 * class and contains the same elements. But it doesn't change when
* this bit set changes. * this bit set changes.
*
* @return the clone of this object. * @return the clone of this object.
*/ */
public Object clone() public Object clone()
{ {
BitSet bs = new BitSet(bits.length * 64); try
System.arraycopy(bits, 0, bs.bits, 0, bits.length); {
BitSet bs = (BitSet) super.clone();
bs.bits = (long[]) bits.clone();
return bs; return bs;
} }
catch (CloneNotSupportedException e)
{
// Impossible to get here.
return null;
}
}
/** /**
* Returns true if the <code>obj</code> is a bit set that contains * Returns true if the <code>obj</code> is a bit set that contains
* exactly the same elements as this bit set, otherwise false. * 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) public boolean equals(Object obj)
{ {
...@@ -183,26 +270,108 @@ public class BitSet implements Cloneable, Serializable ...@@ -183,26 +270,108 @@ public class BitSet implements Cloneable, Serializable
} }
/** /**
* Returns true if the integer <code>bitIndex</code> is in this bit * Sets the bit at the index to the opposite value.
* set, otherwise false. *
* @param bitIndex a non-negative integer * @param index the index of the bit
* @return the value of the bit at the specified index. * @throws IndexOutOfBoundsException if index is negative
* @exception ArrayIndexOutOfBoundsException if the specified bit index * @since 1.4
* is negative.
* @require bitIndex >= 0
*/ */
public boolean get(int pos) public void flip(int index)
{
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 (pos < 0) if (from < 0 || from > to)
throw new IndexOutOfBoundsException(); 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; bits[lo_offset] ^= -1L << from;
int offset = pos / 64; 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) if (offset >= bits.length)
return false; 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;
return (bits[offset] & (1L << bit)) == 0 ? false : true; 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;
}
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;
} }
/** /**
...@@ -233,20 +402,54 @@ public class BitSet implements Cloneable, Serializable ...@@ -233,20 +402,54 @@ public class BitSet implements Cloneable, Serializable
* </pre> * </pre>
* *
* Note that the hash code values changes, if the set is changed. * Note that the hash code values changes, if the set is changed.
*
* @return the hash code value for this bit set. * @return the hash code value for this bit set.
*/ */
public int hashCode() public int hashCode()
{ {
long h = 1234; long h = 1234;
for (int i = bits.length - 1; i >= 0; --i) for (int i = bits.length; i > 0; )
h ^= bits[i] * (i + 1); h ^= i * bits[--i];
return (int) ((h >> 32) ^ h); 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 * Returns the logical number of bits actually used by this bit
* set. It returns the index of the highest set bit plus one. * set. It returns the index of the highest set bit plus one.
* Note that this method doesn't return the number of set bits. * 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() public int length()
...@@ -274,19 +477,87 @@ public class BitSet implements Cloneable, Serializable ...@@ -274,19 +477,87 @@ public class BitSet implements Cloneable, Serializable
} }
/** /**
* 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 * Performs the logical OR operation on this bit set and the
* given <code>set</code>. This means it builds the union * given <code>set</code>. This means it builds the union
* of the two sets. The result is stored into this bit set, which * of the two sets. The result is stored into this bit set, which
* grows as necessary. * grows as necessary.
* @param set the second bit set. *
* @exception OutOfMemoryError if the current set can't grow. * @param bs the second bit set
* @require set != null * @throws NullPointerException if bs is null
*/ */
public void or(BitSet bs) public void or(BitSet bs)
{ {
ensure(bs.bits.length - 1); ensure(bs.bits.length - 1);
int i; for (int i = bs.bits.length - 1; i >= 0; i--)
for (i = 0; i < bs.bits.length; ++i)
bits[i] |= bs.bits[i]; bits[i] |= bs.bits[i];
} }
...@@ -295,25 +566,89 @@ public class BitSet implements Cloneable, Serializable ...@@ -295,25 +566,89 @@ public class BitSet implements Cloneable, Serializable
* the corresponding bit is set to true. If the index was already in * the corresponding bit is set to true. If the index was already in
* the set, this method does nothing. The size of this structure * the set, this method does nothing. The size of this structure
* is automatically increased as necessary. * is automatically increased as necessary.
* @param bitIndex a non-negative integer. *
* @exception ArrayIndexOutOfBoundsException if the specified bit index * @param pos a non-negative integer.
* is negative. * @throws IndexOutOfBoundsException if pos is negative
* @require bitIndex >= 0
*/ */
public void set(int pos) public void set(int pos)
{ {
if (pos < 0) int offset = pos >>> 6;
throw new IndexOutOfBoundsException();
int bit = pos % 64;
int offset = pos / 64;
ensure(offset); 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 * Returns the number of bits actually used by this bit set. Note
* that this method doesn't return the number of set bits. * that this method doesn't return the number of set bits, and that
* @returns the number of bits currently used. * future requests for larger bits will make this automatically grow.
*
* @return the number of bits currently used.
*/ */
public int size() public int size()
{ {
...@@ -324,11 +659,12 @@ public class BitSet implements Cloneable, Serializable ...@@ -324,11 +659,12 @@ public class BitSet implements Cloneable, Serializable
* Returns the string representation of this bit set. This * Returns the string representation of this bit set. This
* consists of a comma separated list of the integers in this set * consists of a comma separated list of the integers in this set
* surrounded by curly braces. There is a space after each comma. * surrounded by curly braces. There is a space after each comma.
* A sample string is thus "{1, 3, 53}".
* @return the string representation. * @return the string representation.
*/ */
public String toString() public String toString()
{ {
String r = "{"; StringBuffer r = new StringBuffer("{");
boolean first = true; boolean first = true;
for (int i = 0; i < bits.length; ++i) for (int i = 0; i < bits.length; ++i)
{ {
...@@ -340,16 +676,15 @@ public class BitSet implements Cloneable, Serializable ...@@ -340,16 +676,15 @@ public class BitSet implements Cloneable, Serializable
{ {
if ((word & bit) != 0) if ((word & bit) != 0)
{ {
if (!first) if (! first)
r += ", "; r.append(", ");
r += Integer.toString(64 * i + j); r.append(64 * i + j);
first = false; first = false;
} }
bit <<= 1; bit <<= 1;
} }
} }
return r.append("}").toString();
return r += "}";
} }
/** /**
...@@ -358,31 +693,29 @@ public class BitSet implements Cloneable, Serializable ...@@ -358,31 +693,29 @@ public class BitSet implements Cloneable, Serializable
* remainder of the two sets (the elements that are in one set, * 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, * but not in the other). The result is stored into this bit set,
* which grows as necessary. * which grows as necessary.
* @param set the second bit set. *
* @exception OutOfMemoryError if the current set can't grow. * @param bs the second bit set
* @require set != null * @throws NullPointerException if bs is null
*/ */
public void xor(BitSet bs) public void xor(BitSet bs)
{ {
ensure(bs.bits.length - 1); ensure(bs.bits.length - 1);
int i; for (int i = bs.bits.length - 1; i >= 0; i--)
for (i = 0; i < bs.bits.length; ++i)
bits[i] ^= bs.bits[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) private final void ensure(int lastElt)
{ {
if (lastElt + 1 > bits.length) if (lastElt >= bits.length)
{ {
long[] nd = new long[lastElt + 1]; long[] nd = new long[lastElt + 1];
System.arraycopy(bits, 0, nd, 0, bits.length); System.arraycopy(bits, 0, nd, 0, bits.length);
bits = nd; bits = nd;
} }
} }
// The actual bits.
long[] bits;
private static final long serialVersionUID = 7997698588986878753L;
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Dictionary.java -- an abstract (and essentially worthless) /* Dictionary.java -- an abstract (and essentially worthless)
class which is Hashtable's superclass 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. This file is part of GNU Classpath.
...@@ -35,49 +35,88 @@ package java.util; ...@@ -35,49 +35,88 @@ package java.util;
* This is an abstract class which has really gone by the wayside. * This is an abstract class which has really gone by the wayside.
* People at Javasoft are probably embarrassed by it. At this point, * 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 * 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 * At any rate, this was what came before the <pre>Map</pre> interface
* in the Collections framework. * 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 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(); public abstract Enumeration elements();
/** /**
* returns the value associated with the supplied key, or null * Returns the value associated with the supplied key, or null
* if no such value exists * 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); 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(); 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(); public abstract Enumeration keys();
/** /**
* inserts a new value into this Dictionary, located by the * Inserts a new value into this Dictionary, located by the
* supllied key; note: Dictionary's subclasses (all 1 of them) * supplied key. Dictionary does not support null keys or values, so
* do not support null keys or values (I can only assume this * a null return can safely be interpreted as adding a new key.
* would have been more general)
* *
* @param key the key which locates the value * @param key the key which locates the value
* @param value the value to put into the Dictionary * @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); 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); 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(); public abstract int size();
} }
...@@ -53,14 +53,16 @@ import java.io.ObjectOutputStream; ...@@ -53,14 +53,16 @@ import java.io.ObjectOutputStream;
* <p> * <p>
* *
* Under ideal circumstances (no collisions), HashMap offers O(1) * 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 * of course, O(n)). In the worst case (all keys map to the same
* hash code -- very unlikely), most operations are O(n). * hash code -- very unlikely), most operations are O(n).
* <p> * <p>
* *
* HashMap is part of the JDK1.2 Collections API. It differs from * 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 * 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> * <p>
* *
* The iterators are <i>fail-fast</i>, meaning that any structural * The iterators are <i>fail-fast</i>, meaning that any structural
...@@ -81,6 +83,7 @@ import java.io.ObjectOutputStream; ...@@ -81,6 +83,7 @@ import java.io.ObjectOutputStream;
* @see IdentityHashMap * @see IdentityHashMap
* @see Hashtable * @see Hashtable
* @since 1.2 * @since 1.2
* @status updated to 1.4
*/ */
public class HashMap extends AbstractMap public class HashMap extends AbstractMap
implements Map, Cloneable, Serializable implements Map, Cloneable, Serializable
...@@ -88,19 +91,16 @@ public class HashMap extends AbstractMap ...@@ -88,19 +91,16 @@ public class HashMap extends AbstractMap
/** /**
* Default number of buckets. This is the value the JDK 1.3 uses. Some * Default number of buckets. This is the value the JDK 1.3 uses. Some
* early documentation specified this value as 101. That is incorrect. * early documentation specified this value as 101. That is incorrect.
* Package visible for use by HashSet.
*/ */
static final int DEFAULT_CAPACITY = 11; static final int DEFAULT_CAPACITY = 11;
/** /**
* The default load factor; this is explicitly specified by the spec. * 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; 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. * Compatible with JDK 1.2.
*/ */
...@@ -108,41 +108,54 @@ public class HashMap extends AbstractMap ...@@ -108,41 +108,54 @@ public class HashMap extends AbstractMap
/** /**
* The rounded product of the capacity and the load factor; when the number * The rounded product of the capacity and the load factor; when the number
* of elements exceeds the threshold, the HashMap calls <pre>rehash()</pre>. * of elements exceeds the threshold, the HashMap calls
* @serial * <code>rehash()</code>.
* @serial the threshold for rehashing
*/ */
int threshold; private int threshold;
/** /**
* Load factor of this HashMap: used in computing the 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; final float loadFactor;
/** /**
* Array containing the actual key-value mappings. * Array containing the actual key-value mappings.
* Package visible for use by nested and subclasses.
*/ */
transient HashEntry[] buckets; transient HashEntry[] buckets;
/** /**
* Counts the number of modifications this HashMap has undergone, used * Counts the number of modifications this HashMap has undergone, used
* by Iterators to know when to throw ConcurrentModificationExceptions. * by Iterators to know when to throw ConcurrentModificationExceptions.
* Package visible for use by nested and subclasses.
*/ */
transient int modCount; transient int modCount;
/** /**
* The size of this HashMap: denotes the number of key-value pairs. * The size of this HashMap: denotes the number of key-value pairs.
* Package visible for use by nested and subclasses.
*/ */
transient int size; 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 * Class to represent an entry in the hash table. Holds a single key-value
* pair. This is extended again in LinkedHashMap. See {@link clone()} * pair. Package visible for use by subclass.
* for why this must be Cloneable. *
* @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; HashEntry next;
/** /**
...@@ -158,7 +171,8 @@ public class HashMap extends AbstractMap ...@@ -158,7 +171,8 @@ public class HashMap extends AbstractMap
/** /**
* Called when this entry is removed from the map. This version simply * Called when this entry is removed from the map. This version simply
* returns the value, but in LinkedHashMap, it must also do bookkeeping. * 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() Object cleanup()
{ {
...@@ -182,9 +196,8 @@ public class HashMap extends AbstractMap ...@@ -182,9 +196,8 @@ public class HashMap extends AbstractMap
* *
* Every element in Map m will be put into this new HashMap. * Every element in Map m will be put into this new HashMap.
* *
* @param m a Map whose key / value pairs will be put into * @param m a Map whose key / value pairs will be put into the new HashMap.
* the new HashMap. <b>NOTE: key / value pairs * <b>NOTE: key / value pairs are not cloned in this constructor.</b>
* are not cloned in this constructor.</b>
* @throws NullPointerException if m is null * @throws NullPointerException if m is null
*/ */
public HashMap(Map m) public HashMap(Map m)
...@@ -197,8 +210,8 @@ public class HashMap extends AbstractMap ...@@ -197,8 +210,8 @@ public class HashMap extends AbstractMap
* Construct a new HashMap with a specific inital capacity and * Construct a new HashMap with a specific inital capacity and
* default load factor of 0.75. * default load factor of 0.75.
* *
* @param initialCapacity the initial capacity of this HashMap (>=0) * @param initialCapacity the initial capacity of this HashMap (&gt;=0)
* @throws IllegalArgumentException if (initialCapacity < 0) * @throws IllegalArgumentException if (initialCapacity &lt; 0)
*/ */
public HashMap(int initialCapacity) public HashMap(int initialCapacity)
{ {
...@@ -208,10 +221,10 @@ public class HashMap extends AbstractMap ...@@ -208,10 +221,10 @@ public class HashMap extends AbstractMap
/** /**
* Construct a new HashMap with a specific inital capacity and load factor. * Construct a new HashMap with a specific inital capacity and load factor.
* *
* @param initialCapacity the initial capacity (>=0) * @param initialCapacity the initial capacity (&gt;=0)
* @param loadFactor the load factor (>0, not NaN) * @param loadFactor the load factor (&gt; 0, not NaN)
* @throws IllegalArgumentException if (initialCapacity < 0) || * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
* ! (loadFactor > 0.0) * ! (loadFactor &gt; 0.0)
*/ */
public HashMap(int initialCapacity, float loadFactor) public HashMap(int initialCapacity, float loadFactor)
{ {
...@@ -229,7 +242,8 @@ public class HashMap extends AbstractMap ...@@ -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 * @return the size
*/ */
public int size() public int size()
...@@ -238,7 +252,8 @@ public class HashMap extends AbstractMap ...@@ -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> * @return <code>size() == 0</code>
*/ */
public boolean isEmpty() public boolean isEmpty()
...@@ -247,29 +262,31 @@ public class HashMap extends AbstractMap ...@@ -247,29 +262,31 @@ public class HashMap extends AbstractMap
} }
/** /**
* Returns true if this HashMap contains a value <pre>o</pre>, such that * Return the value in this HashMap associated with the supplied key,
* <pre>o.equals(value)</pre>. * 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 * @param key the key for which to fetch an associated value
* @return true if at least one key maps to the 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--)
{ {
HashEntry e = buckets[i]; int idx = hash(key);
HashEntry e = buckets[idx];
while (e != null) while (e != null)
{ {
if (value == null ? e.value == null : value.equals(e.value)) if (equals(key, e.key))
return true; return e.value;
e = e.next; e = e.next;
} }
} return null;
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 HashMap. * in this HashMap.
* *
* @param key the key to search for in this HashMap * @param key the key to search for in this HashMap
...@@ -282,7 +299,7 @@ public class HashMap extends AbstractMap ...@@ -282,7 +299,7 @@ public class HashMap extends AbstractMap
HashEntry e = buckets[idx]; HashEntry e = buckets[idx];
while (e != null) while (e != null)
{ {
if (key == null ? e.key == null : key.equals(e.key)) if (equals(key, e.key))
return true; return true;
e = e.next; e = e.next;
} }
...@@ -290,30 +307,6 @@ public class HashMap extends AbstractMap ...@@ -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. * Puts the supplied value into the Map, mapped by the supplied key.
* The value may be retrieved by any object which <code>equals()</code> * 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 * this key. NOTE: Since the prior value could also be null, you must
...@@ -328,13 +321,12 @@ public class HashMap extends AbstractMap ...@@ -328,13 +321,12 @@ public class HashMap extends AbstractMap
*/ */
public Object put(Object key, Object value) public Object put(Object key, Object value)
{ {
modCount++;
int idx = hash(key); int idx = hash(key);
HashEntry e = buckets[idx]; HashEntry e = buckets[idx];
while (e != null) 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. // Must use this method for necessary bookkeeping in LinkedHashMap.
return e.setValue(value); return e.setValue(value);
else else
...@@ -342,6 +334,7 @@ public class HashMap extends AbstractMap ...@@ -342,6 +334,7 @@ public class HashMap extends AbstractMap
} }
// At this point, we know we need to add a new entry. // At this point, we know we need to add a new entry.
modCount++;
if (++size > threshold) if (++size > threshold)
{ {
rehash(); rehash();
...@@ -355,27 +348,36 @@ public class HashMap extends AbstractMap ...@@ -355,27 +348,36 @@ public class HashMap extends AbstractMap
} }
/** /**
* Helper method for put, that creates and adds a new Entry. This is * Copies all elements of the given map into this hashtable. If this table
* overridden in LinkedHashMap for bookkeeping purposes. * already has a mapping for a key, the new mapping replaces the current
* one.
* *
* @param key the key of the new Entry * @param m the map to be hashed into this
* @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)
*/ */
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]; for (int msize = m.size(); msize > 0; msize--)
buckets[idx] = e; {
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 * 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 * 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 * could also be null, you must use containsKey to see if you are
* actually removing a mapping. * actually removing a mapping.
* *
...@@ -384,15 +386,15 @@ public class HashMap extends AbstractMap ...@@ -384,15 +386,15 @@ public class HashMap extends AbstractMap
*/ */
public Object remove(Object key) public Object remove(Object key)
{ {
modCount++;
int idx = hash(key); int idx = hash(key);
HashEntry e = buckets[idx]; HashEntry e = buckets[idx];
HashEntry last = null; HashEntry last = null;
while (e != null) while (e != null)
{ {
if (key == null ? e.key == null : key.equals(e.key)) if (equals(key, e.key))
{ {
modCount++;
if (last == null) if (last == null)
buckets[idx] = e.next; buckets[idx] = e.next;
else else
...@@ -408,40 +410,39 @@ public class HashMap extends AbstractMap ...@@ -408,40 +410,39 @@ public class HashMap extends AbstractMap
} }
/** /**
* Copies all elements of the given map into this hashtable. If this table * Clears the Map so it has no keys. This is O(1).
* already has a mapping for a key, the new mapping replaces the current
* one.
*
* @param m the map to be hashed into this
*/ */
public void putAll(Map m) public void clear()
{
Iterator itr = m.entrySet().iterator();
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; if (size != 0)
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++; for (int i = buckets.length - 1; i >= 0; i--)
Arrays.fill(buckets, null); {
size = 0; 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 ...@@ -463,6 +464,8 @@ public class HashMap extends AbstractMap
} }
copy.buckets = new HashEntry[buckets.length]; copy.buckets = new HashEntry[buckets.length];
copy.putAllInternal(this); copy.putAllInternal(this);
// Clear the entry cache. AbstractMap.clone() does the others.
copy.entries = null;
return copy; return copy;
} }
...@@ -477,9 +480,10 @@ public class HashMap extends AbstractMap ...@@ -477,9 +480,10 @@ public class HashMap extends AbstractMap
*/ */
public Set keySet() public Set keySet()
{ {
// Create an AbstractSet with custom implementations of those methods that if (keys == null)
// can be overridden easily and efficiently. // Create an AbstractSet with custom implementations of those methods
return new AbstractSet() // that can be overridden easily and efficiently.
keys = new AbstractSet()
{ {
public int size() public int size()
{ {
...@@ -499,19 +503,20 @@ public class HashMap extends AbstractMap ...@@ -499,19 +503,20 @@ public class HashMap extends AbstractMap
public boolean contains(Object o) public boolean contains(Object o)
{ {
return HashMap.this.containsKey(o); return containsKey(o);
} }
public boolean remove(Object o) public boolean remove(Object o)
{ {
// Test against the size of the HashMap to determine if anything // Test against the size of the HashMap to determine if anything
// really got removed. This is necessary because the return value of // really got removed. This is neccessary because the return value
// HashMap.remove() is ambiguous in the null case. // of HashMap.remove() is ambiguous in the null case.
int oldsize = size; int oldsize = size;
HashMap.this.remove(o); HashMap.this.remove(o);
return (oldsize != size); return oldsize != size;
} }
}; };
return keys;
} }
/** /**
...@@ -526,9 +531,10 @@ public class HashMap extends AbstractMap ...@@ -526,9 +531,10 @@ public class HashMap extends AbstractMap
*/ */
public Collection values() public Collection values()
{ {
if (values == null)
// We don't bother overriding many of the optional methods, as doing so // We don't bother overriding many of the optional methods, as doing so
// wouldn't provide any significant performance advantage. // wouldn't provide any significant performance advantage.
return new AbstractCollection() values = new AbstractCollection()
{ {
public int size() public int size()
{ {
...@@ -546,13 +552,13 @@ public class HashMap extends AbstractMap ...@@ -546,13 +552,13 @@ public class HashMap extends AbstractMap
HashMap.this.clear(); HashMap.this.clear();
} }
}; };
return values;
} }
/** /**
* Returns a "set view" of this HashMap's entries. The set is backed by * 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 * the HashMap, so changes in one show up in the other. The set supports
* element removal, but not element addition. * element removal, but not element addition.<p>
* <p>
* *
* Note that the iterators for all three views, from keySet(), entrySet(), * Note that the iterators for all three views, from keySet(), entrySet(),
* and values(), traverse the HashMap in the same sequence. * and values(), traverse the HashMap in the same sequence.
...@@ -564,9 +570,10 @@ public class HashMap extends AbstractMap ...@@ -564,9 +570,10 @@ public class HashMap extends AbstractMap
*/ */
public Set entrySet() public Set entrySet()
{ {
// Create an AbstractSet with custom implementations of those methods that if (entries == null)
// can be overridden easily and efficiently. // Create an AbstractSet with custom implementations of those methods
return new AbstractSet() // that can be overridden easily and efficiently.
entries = new AbstractSet()
{ {
public int size() public int size()
{ {
...@@ -600,17 +607,25 @@ public class HashMap extends AbstractMap ...@@ -600,17 +607,25 @@ public class HashMap extends AbstractMap
return false; 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 * @param key the key of the new Entry
* @return the bucket number * @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 ...@@ -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 * 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 * indices; this is called when the addition of a new value would cause
* size() > threshold. Note that the existing Entry objects are reused in * size() > threshold. Note that the existing Entry objects are reused in
...@@ -682,35 +743,6 @@ public class HashMap extends AbstractMap ...@@ -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. * Serializes this object to the given stream.
* *
* @param s the stream to write to * @param s the stream to write to
...@@ -757,9 +789,6 @@ public class HashMap extends AbstractMap ...@@ -757,9 +789,6 @@ public class HashMap extends AbstractMap
// Read and use capacity. // Read and use capacity.
buckets = new HashEntry[s.readInt()]; buckets = new HashEntry[s.readInt()];
int len = s.readInt(); int len = s.readInt();
// Already happens automatically.
// size = 0;
// modCount = 0;
// Read and use key/value pairs. // Read and use key/value pairs.
for ( ; len > 0; len--) for ( ; len > 0; len--)
...@@ -773,29 +802,29 @@ public class HashMap extends AbstractMap ...@@ -773,29 +802,29 @@ public class HashMap extends AbstractMap
* *
* @author Jon Zeppieri * @author Jon Zeppieri
*/ */
class HashIterator implements Iterator private final class HashIterator implements Iterator
{ {
/** /**
* The type of this Iterator: {@link #KEYS}, {@link #VALUES}, * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
* or {@link #ENTRIES}. * or {@link #ENTRIES}.
*/ */
final int type; private final int type;
/** /**
* The number of modifications to the backing HashMap that we know about. * 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(). */ /** The number of elements remaining to be returned by next(). */
int count = size; private int count = size;
/** Current index in the physical hash table. */ /** Current index in the physical hash table. */
int idx = buckets.length; private int idx = buckets.length;
/** The last Entry returned by a next() call. */ /** 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 * 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 * if we're iterating through a bucket that contains multiple linked
* entries. It is null if next() needs to find a new bucket. * 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. * Construct a new HashIterator with the supplied type.
...@@ -840,14 +869,14 @@ public class HashMap extends AbstractMap ...@@ -840,14 +869,14 @@ public class HashMap extends AbstractMap
last = e; last = e;
if (type == VALUES) if (type == VALUES)
return e.value; return e.value;
else if (type == KEYS) if (type == KEYS)
return e.key; return e.key;
return e; return e;
} }
/** /**
* Removes from the backing HashMap the last element which was fetched * 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 ConcurrentModificationException if the HashMap was modified
* @throws IllegalStateException if called when there is no last element * @throws IllegalStateException if called when there is no last element
*/ */
...@@ -859,8 +888,8 @@ public class HashMap extends AbstractMap ...@@ -859,8 +888,8 @@ public class HashMap extends AbstractMap
throw new IllegalStateException(); throw new IllegalStateException();
HashMap.this.remove(last.key); HashMap.this.remove(last.key);
knownMod++;
last = null; last = null;
knownMod++;
} }
} }
} }
/* HashSet.java -- a class providing a HashMap-backet Set /* HashSet.java -- a class providing a HashMap-backed Set
Copyright (C) 1998, 1999 Free Software Foundation, Inc. Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
This file is part of GNU Classpath. This file is part of GNU Classpath.
...@@ -33,87 +33,115 @@ import java.io.ObjectInputStream; ...@@ -33,87 +33,115 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
/** /**
* This class provides a HashMap-backed implementation of the * This class provides a HashMap-backed implementation of the Set interface.
* Set interface. * <p>
*
* 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.
* *
* Most operations are O(1), assuming no hash collisions. In the worst * 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 public class HashSet extends AbstractSet
implements Set, Cloneable, Serializable implements Set, Cloneable, Serializable
{ {
/** the HashMap which backs this Set */ /**
transient HashMap map; * Compatible with JDK 1.2.
static final long serialVersionUID = -5024744406713321676L; */
private static final long serialVersionUID = -5024744406713321676L;
/**
* The HashMap which backs this Set.
*/
private transient HashMap map;
/** /**
* construct a new, empty HashSet whose backing HashMap has the default * Construct a new, empty HashSet whose backing HashMap has the default
* capacity and loadFacor * capacity (11) and loadFacor (0.75).
*/ */
public HashSet() public HashSet()
{ {
map = new HashMap(); this(HashMap.DEFAULT_CAPACITY, HashMap.DEFAULT_LOAD_FACTOR);
} }
/** /**
* construct a new, empty HashSet whose backing HashMap has the supplied * Construct a new, empty HashSet whose backing HashMap has the supplied
* capacity and the default load factor * capacity and the default load factor (0.75).
* *
* @param initialCapacity the initial capacity of the backing * @param initialCapacity the initial capacity of the backing HashMap
* HashMap * @throws IllegalArgumentException if the capacity is negative
*/ */
public HashSet(int initialCapacity) 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 * Construct a new, empty HashSet whose backing HashMap has the supplied
* capacity and load factor * capacity and load factor.
* *
* @param initialCapacity the initial capacity of the backing * @param initialCapacity the initial capacity of the backing HashMap
* HashMap
* @param loadFactor the load factor 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) 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 * Construct a new HashSet with the same elements as are in the supplied
* collection (eliminating any duplicates, of course; the backing HashMap * collection (eliminating any duplicates, of course). The backing storage
* will have the default capacity and load factor * 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 * @param c a collection of initial set elements
* which this set will be initialized * @throws NullPointerException if c is null
*/ */
public HashSet(Collection c) public HashSet(Collection c)
{ {
map = new HashMap(); this(Math.max(2 * c.size(), HashMap.DEFAULT_CAPACITY));
addAll(c); addAll(c);
} }
/** /**
* adds the given Object to the set if it is not already in the Set, * Adds the given Object to the set if it is not already in the Set.
* returns true if teh element was added, false otherwise * 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) 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() public void clear()
{ {
...@@ -121,8 +149,10 @@ public class HashSet extends AbstractSet ...@@ -121,8 +149,10 @@ public class HashSet extends AbstractSet
} }
/** /**
* returns a shallow copy of this Set (the Set itself is cloned; its * Returns a shallow copy of this Set. The Set itself is cloned; its
* elements are not) * elements are not.
*
* @return a shallow clone of the set
*/ */
public Object clone() public Object clone()
{ {
...@@ -133,15 +163,17 @@ public class HashSet extends AbstractSet ...@@ -133,15 +163,17 @@ public class HashSet extends AbstractSet
} }
catch (CloneNotSupportedException x) catch (CloneNotSupportedException x)
{ {
// Impossible to get here.
} }
copy.map = (HashMap) map.clone(); copy.map = (HashMap) map.clone();
return copy; 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) public boolean contains(Object o)
{ {
...@@ -149,25 +181,35 @@ public class HashSet extends AbstractSet ...@@ -149,25 +181,35 @@ public class HashSet extends AbstractSet
} }
/** /**
* 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() public boolean isEmpty()
{ {
return map.isEmpty(); return map.size == 0;
} }
/** /**
* returns an Iterator over the elements of this Set; the Iterator allows * Returns an Iterator over the elements of this Set, which visits the
* removal of elements * 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() 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 * Removes the supplied Object from this Set if it is in the Set.
* true if an element was removed, false otherwise *
* @param o the object to remove
* @return true if an element was removed
*/ */
public boolean remove(Object o) public boolean remove(Object o)
{ {
...@@ -175,18 +217,42 @@ public class HashSet extends AbstractSet ...@@ -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() public int size()
{ {
return map.size(); return map.size;
}
/**
* 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);
} }
/** Serialize this Object in a manner which is binary-compatible with the /**
* JDK */ * 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 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.writeInt(map.buckets.length);
s.writeFloat(map.loadFactor); s.writeFloat(map.loadFactor);
s.writeInt(map.size); s.writeInt(map.size);
...@@ -194,25 +260,23 @@ public class HashSet extends AbstractSet ...@@ -194,25 +260,23 @@ public class HashSet extends AbstractSet
s.writeObject(it.next()); s.writeObject(it.next());
} }
/** Deserialize this Object in a manner which is binary-compatible with /**
* the JDK */ * Deserializes this object from the given stream.
private void readObject(ObjectInputStream s) throws IOException, *
ClassNotFoundException * @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; s.defaultReadObject();
float loadFactor;
Object element;
capacity = s.readInt();
loadFactor = s.readFloat();
size = s.readInt();
map = new HashMap(capacity, loadFactor); map = init(s.readInt(), s.readFloat());
for (int size = s.readInt(); size > 0; size--)
for (i = 0; i < size; i++) map.put(s.readObject(), "");
{
element = s.readObject();
map.put(element, Boolean.TRUE);
}
} }
} }
...@@ -66,7 +66,9 @@ import java.io.ObjectOutputStream; ...@@ -66,7 +66,9 @@ import java.io.ObjectOutputStream;
* Unlike HashMap, Hashtable does not accept `null' as a key value. Also, * Unlike HashMap, Hashtable does not accept `null' as a key value. Also,
* all accesses are synchronized: in a single thread environment, this is * all accesses are synchronized: in a single thread environment, this is
* expensive, but in a multi-thread environment, this saves you the effort * 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> * <p>
* *
* The iterators are <i>fail-fast</i>, meaning that any structural * The iterators are <i>fail-fast</i>, meaning that any structural
...@@ -84,6 +86,7 @@ import java.io.ObjectOutputStream; ...@@ -84,6 +86,7 @@ import java.io.ObjectOutputStream;
* @see IdentityHashMap * @see IdentityHashMap
* @see LinkedHashMap * @see LinkedHashMap
* @since 1.0 * @since 1.0
* @status updated to 1.4
*/ */
public class Hashtable extends Dictionary public class Hashtable extends Dictionary
implements Map, Cloneable, Serializable implements Map, Cloneable, Serializable
...@@ -93,6 +96,12 @@ public class Hashtable extends Dictionary ...@@ -93,6 +96,12 @@ public class Hashtable extends Dictionary
*/ */
private static final int DEFAULT_CAPACITY = 11; 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. * The default load factor; this is explicitly specified by the spec.
*/ */
...@@ -106,39 +115,57 @@ public class Hashtable extends Dictionary ...@@ -106,39 +115,57 @@ public class Hashtable extends Dictionary
/** /**
* The rounded product of the capacity and the load factor; when the number * The rounded product of the capacity and the load factor; when the number
* of elements exceeds the threshold, the Hashtable calls * of elements exceeds the threshold, the Hashtable calls
* <pre>rehash()</pre>. * <code>rehash()</code>.
* @serial * @serial
*/ */
int threshold; private int threshold;
/** /**
* Load factor of this Hashtable: used in computing the threshold. * Load factor of this Hashtable: used in computing the threshold.
* @serial * @serial
*/ */
final float loadFactor; private final float loadFactor;
/** /**
* Array containing the actual key-value mappings. * Array containing the actual key-value mappings.
*/ */
// Package visible for use by nested classes.
transient HashEntry[] buckets; transient HashEntry[] buckets;
/** /**
* Counts the number of modifications this Hashtable has undergone, used * Counts the number of modifications this Hashtable has undergone, used
* by Iterators to know when to throw ConcurrentModificationExceptions. * by Iterators to know when to throw ConcurrentModificationExceptions.
*/ */
// Package visible for use by nested classes.
transient int modCount; transient int modCount;
/** /**
* The size of this Hashtable: denotes the number of key-value pairs. * The size of this Hashtable: denotes the number of key-value pairs.
*/ */
// Package visible for use by nested classes.
transient int size; 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 * 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 * pair. A Hashtable Entry is identical to a HashMap Entry, except that
* `null' is not allowed for keys and values. * `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. */ /** The next entry in the linked list. */
HashEntry next; HashEntry next;
...@@ -159,7 +186,7 @@ public class Hashtable extends Dictionary ...@@ -159,7 +186,7 @@ public class Hashtable extends Dictionary
* @return the prior value * @return the prior value
* @throws NullPointerException if <code>newVal</code> is null * @throws NullPointerException if <code>newVal</code> is null
*/ */
public final Object setValue(Object newVal) public Object setValue(Object newVal)
{ {
if (newVal == null) if (newVal == null)
throw new NullPointerException(); throw new NullPointerException();
...@@ -193,15 +220,15 @@ public class Hashtable extends Dictionary ...@@ -193,15 +220,15 @@ public class Hashtable extends Dictionary
public Hashtable(Map m) public Hashtable(Map m)
{ {
this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); 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 * Construct a new Hashtable with a specific inital capacity and
* default load factor of 0.75. * default load factor of 0.75.
* *
* @param initialCapacity the initial capacity of this Hashtable (>=0) * @param initialCapacity the initial capacity of this Hashtable (&gt;= 0)
* @throws IllegalArgumentException if (initialCapacity < 0) * @throws IllegalArgumentException if (initialCapacity &lt; 0)
*/ */
public Hashtable(int initialCapacity) public Hashtable(int initialCapacity)
{ {
...@@ -212,10 +239,10 @@ public class Hashtable extends Dictionary ...@@ -212,10 +239,10 @@ public class Hashtable extends Dictionary
* Construct a new Hashtable with a specific initial capacity and * Construct a new Hashtable with a specific initial capacity and
* load factor. * load factor.
* *
* @param initialCapacity the initial capacity (>=0) * @param initialCapacity the initial capacity (&gt;= 0)
* @param loadFactor the load factor (>0, not NaN) * @param loadFactor the load factor (&gt; 0, not NaN)
* @throws IllegalArgumentException if (initialCapacity < 0) || * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
* ! (loadFactor > 0.0) * ! (loadFactor &gt; 0.0)
*/ */
public Hashtable(int initialCapacity, float loadFactor) public Hashtable(int initialCapacity, float loadFactor)
{ {
...@@ -251,30 +278,36 @@ public class Hashtable extends Dictionary ...@@ -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 * @return the keys
* @see #elements() * @see #elements()
* @see #keySet() * @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 * @return the values
* @see #keys() * @see #keys()
* @see #values() * @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>, * Returns true if this Hashtable contains a value <code>o</code>,
* such that <pre>o.equals(value)</pre>. This is the same as * such that <code>o.equals(value)</code>. This is the same as
* <code>containsValue()</code>, and is O(n). * <code>containsValue()</code>, and is O(n).
* <p> * <p>
* *
...@@ -284,48 +317,46 @@ public class Hashtable extends Dictionary ...@@ -284,48 +317,46 @@ public class Hashtable extends Dictionary
* *
* @param value the value to search for in this Hashtable * @param value the value to search for in this Hashtable
* @return true if at least one key maps to the value * @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 #containsValue(Object)
* @see #containsKey(Object) * @see #containsKey(Object)
*/ */
public synchronized boolean contains(Object value) public synchronized boolean contains(Object value)
{ {
// Check if value is null in case Hashtable is empty. // Check if value is null.
if (value == null) if (value == null)
throw new NullPointerException(); throw new NullPointerException();
return containsValue(value);
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;
} }
/** /**
* Returns true if this Hashtable contains a value <pre>o</pre>, such that * Returns true if this Hashtable contains a value <code>o</code>, such that
* <pre>o.equals(value)</pre>. This is the new API for the old * <code>o.equals(value)</code>. This is the new API for the old
* <code>contains()</code>. * <code>contains()</code>, except that it is forgiving of null.
* *
* @param value the value to search for in this Hashtable * @param value the value to search for in this Hashtable
* @return true if at least one key maps to the value * @return true if at least one key maps to the value
* @throws NullPointerException if <pre>value</pre> is null
* @see #contains(Object) * @see #contains(Object)
* @see #containsKey(Object) * @see #containsKey(Object)
* @since 1.2 * @since 1.2
*/ */
public boolean containsValue(Object value) 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. * in this Hashtable.
* *
* @param key the key to search for in this Hashtable * @param key the key to search for in this Hashtable
...@@ -348,7 +379,7 @@ public class Hashtable extends Dictionary ...@@ -348,7 +379,7 @@ public class Hashtable extends Dictionary
/** /**
* Return the value in this Hashtable associated with the supplied key, * 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 * @param key the key for which to fetch an associated value
* @return what the key maps to, if present * @return what the key maps to, if present
...@@ -383,7 +414,6 @@ public class Hashtable extends Dictionary ...@@ -383,7 +414,6 @@ public class Hashtable extends Dictionary
*/ */
public synchronized Object put(Object key, Object value) public synchronized Object put(Object key, Object value)
{ {
modCount++;
int idx = hash(key); int idx = hash(key);
HashEntry e = buckets[idx]; HashEntry e = buckets[idx];
...@@ -407,6 +437,7 @@ public class Hashtable extends Dictionary ...@@ -407,6 +437,7 @@ public class Hashtable extends Dictionary
} }
// At this point, we know we need to add a new entry. // At this point, we know we need to add a new entry.
modCount++;
if (++size > threshold) if (++size > threshold)
{ {
rehash(); rehash();
...@@ -425,15 +456,18 @@ public class Hashtable extends Dictionary ...@@ -425,15 +456,18 @@ public class Hashtable extends Dictionary
/** /**
* Removes from the table and returns the value which is mapped by the * 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 * 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 * @param key the key used to locate the value to remove
* @return whatever the key mapped to, if present * @return whatever the key mapped to, if present
* @throws NullPointerException if key is null
*/ */
public synchronized Object remove(Object key) public synchronized Object remove(Object key)
{ {
modCount++; if (key == null)
return null;
int idx = hash(key); int idx = hash(key);
HashEntry e = buckets[idx]; HashEntry e = buckets[idx];
HashEntry last = null; HashEntry last = null;
...@@ -442,6 +476,7 @@ public class Hashtable extends Dictionary ...@@ -442,6 +476,7 @@ public class Hashtable extends Dictionary
{ {
if (key.equals(e.key)) if (key.equals(e.key))
{ {
modCount++;
if (last == null) if (last == null)
buckets[idx] = e.next; buckets[idx] = e.next;
else else
...@@ -488,10 +523,13 @@ public class Hashtable extends Dictionary ...@@ -488,10 +523,13 @@ public class Hashtable extends Dictionary
*/ */
public synchronized void clear() public synchronized void clear()
{ {
if (size > 0)
{
modCount++; modCount++;
Arrays.fill(buckets, null); Arrays.fill(buckets, null);
size = 0; size = 0;
} }
}
/** /**
* Returns a shallow clone of this Hashtable. The Map itself is cloned, * Returns a shallow clone of this Hashtable. The Map itself is cloned,
...@@ -511,36 +549,18 @@ public class Hashtable extends Dictionary ...@@ -511,36 +549,18 @@ public class Hashtable extends Dictionary
// This is impossible. // This is impossible.
} }
copy.buckets = new HashEntry[buckets.length]; copy.buckets = new HashEntry[buckets.length];
copy.putAllInternal(this);
for (int i = buckets.length - 1; i >= 0; i--) // Clear the caches.
{ copy.keys = null;
HashEntry e = buckets[i]; copy.values = null;
HashEntry last = null; copy.entries = 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;
}
}
return copy; return copy;
} }
/** /**
* Converts this Hashtable to a String, surrounded by braces (<pre>'{'</pre> * Converts this Hashtable to a String, surrounded by braces, and with
* and <pre>'}'</pre>), key/value pairs listed with an equals sign between, * key/value pairs listed with an equals sign between, separated by a
* (<pre>'='</pre>), and pairs separated by comma and space * comma and space. For example, <code>"{a=1, b=2}"</code>.<p>
* (<pre>", "</pre>).
* <p>
* *
* NOTE: if the <code>toString()</code> method of any key or value * NOTE: if the <code>toString()</code> method of any key or value
* throws an exception, this will fail for the same reason. * throws an exception, this will fail for the same reason.
...@@ -552,7 +572,7 @@ public class Hashtable extends Dictionary ...@@ -552,7 +572,7 @@ public class Hashtable extends Dictionary
// Since we are already synchronized, and entrySet().iterator() // Since we are already synchronized, and entrySet().iterator()
// would repeatedly re-lock/release the monitor, we directly use the // would repeatedly re-lock/release the monitor, we directly use the
// unsynchronized HashIterator instead. // unsynchronized HashIterator instead.
Iterator entries = new HashIterator(HashIterator.ENTRIES); Iterator entries = new HashIterator(ENTRIES);
StringBuffer r = new StringBuffer("{"); StringBuffer r = new StringBuffer("{");
for (int pos = size; pos > 0; pos--) for (int pos = size; pos > 0; pos--)
{ {
...@@ -568,9 +588,11 @@ public class Hashtable extends Dictionary ...@@ -568,9 +588,11 @@ public class Hashtable extends Dictionary
* Returns a "set view" of this Hashtable's keys. The set is backed by * 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 * the hashtable, so changes in one show up in the other. The set supports
* element removal, but not element addition. The set is properly * element removal, but not element addition. The set is properly
* synchronized on the original hashtable. The set will throw a * synchronized on the original hashtable. Sun has not documented the
* {@link NullPointerException} if null is passed to <code>contains</code>, * proper interaction of null with this set, but has inconsistent behavior
* <code>remove</code>, or related methods. * 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 * @return a set view of the keys
* @see #values() * @see #values()
...@@ -579,8 +601,10 @@ public class Hashtable extends Dictionary ...@@ -579,8 +601,10 @@ public class Hashtable extends Dictionary
*/ */
public Set keySet() public Set keySet()
{ {
// Create a synchronized AbstractSet with custom implementations of those if (keys == null)
// methods that can be overridden easily and efficiently. {
// Create a synchronized AbstractSet with custom implementations of
// those methods that can be overridden easily and efficiently.
Set r = new AbstractSet() Set r = new AbstractSet()
{ {
public int size() public int size()
...@@ -590,7 +614,7 @@ public class Hashtable extends Dictionary ...@@ -590,7 +614,7 @@ public class Hashtable extends Dictionary
public Iterator iterator() public Iterator iterator()
{ {
return new HashIterator(HashIterator.KEYS); return new HashIterator(KEYS);
} }
public void clear() public void clear()
...@@ -600,29 +624,33 @@ public class Hashtable extends Dictionary ...@@ -600,29 +624,33 @@ public class Hashtable extends Dictionary
public boolean contains(Object o) public boolean contains(Object o)
{ {
return Hashtable.this.containsKey(o); if (o == null)
return false;
return containsKey(o);
} }
public boolean remove(Object o) public boolean remove(Object o)
{ {
return (Hashtable.this.remove(o) != null); return Hashtable.this.remove(o) != null;
} }
}; };
// We must specify the correct object to synchronize upon, hence the // We must specify the correct object to synchronize upon, hence the
// use of a non-public API // use of a non-public API
return new Collections.SynchronizedSet(this, r); keys = new Collections.SynchronizedSet(this, r);
}
return keys;
} }
/** /**
* Returns a "collection view" (or "bag view") of this Hashtable's values. * 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 * The collection is backed by the hashtable, so changes in one show up
* in the other. The collection supports element removal, but not element * in the other. The collection supports element removal, but not element
* addition. The collection is properly synchronized on the original * addition. The collection is properly synchronized on the original
* hashtable. The collection will throw a {@link NullPointerException} * hashtable. Sun has not documented the proper interaction of null with
* if null is passed to <code>contains</code> or related methods, but not * this set, but has inconsistent behavior in the JDK. Therefore, in this
* if passed to <code>remove</code> or related methods. * 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 * @return a bag view of the values
* @see #keySet() * @see #keySet()
...@@ -631,6 +659,8 @@ public class Hashtable extends Dictionary ...@@ -631,6 +659,8 @@ public class Hashtable extends Dictionary
*/ */
public Collection values() public Collection values()
{ {
if (values == null)
{
// We don't bother overriding many of the optional methods, as doing so // We don't bother overriding many of the optional methods, as doing so
// wouldn't provide any significant performance advantage. // wouldn't provide any significant performance advantage.
Collection r = new AbstractCollection() Collection r = new AbstractCollection()
...@@ -642,35 +672,32 @@ public class Hashtable extends Dictionary ...@@ -642,35 +672,32 @@ public class Hashtable extends Dictionary
public Iterator iterator() public Iterator iterator()
{ {
return new HashIterator(HashIterator.VALUES); return new HashIterator(VALUES);
} }
public void clear() public void clear()
{ {
Hashtable.this.clear(); Hashtable.this.clear();
} }
// Override this so that we check for null
public boolean contains(Object o)
{
return Hashtable.this.contains(o);
}
}; };
// We must specify the correct object to synchronize upon, hence the // We must specify the correct object to synchronize upon, hence the
// use of a non-public API // use of a non-public API
return new Collections.SynchronizedCollection(this, r); values = new Collections.SynchronizedCollection(this, r);
}
return values;
} }
/** /**
* Returns a "set view" of this Hashtable's entries. The set is backed by * 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 * the hashtable, so changes in one show up in the other. The set supports
* element removal, but not element addition. The set is properly * element removal, but not element addition. The set is properly
* synchronized on the original hashtable. The set will throw a * synchronized on the original hashtable. Sun has not documented the
* {@link NullPointerException} if the Map.Entry passed to * proper interaction of null with this set, but has inconsistent behavior
* <code>contains</code>, <code>remove</code>, or related methods returns * in the JDK. Therefore, in this implementation, contains, remove,
* null for <code>getKey</code>, but not if the Map.Entry is null or * containsAll, retainAll, removeAll, and equals just ignore a null entry,
* returns null for <code>getValue</code>. * or an entry with a null key or value, rather than throwing a
* {@link NullPointerException}. However, calling entry.setValue(null)
* will fail.
* <p> * <p>
* *
* Note that the iterators for all three views, from keySet(), entrySet(), * Note that the iterators for all three views, from keySet(), entrySet(),
...@@ -684,8 +711,10 @@ public class Hashtable extends Dictionary ...@@ -684,8 +711,10 @@ public class Hashtable extends Dictionary
*/ */
public Set entrySet() public Set entrySet()
{ {
// Create an AbstractSet with custom implementations of those methods that if (entries == null)
// can be overridden easily and efficiently. {
// Create an AbstractSet with custom implementations of those methods
// that can be overridden easily and efficiently.
Set r = new AbstractSet() Set r = new AbstractSet()
{ {
public int size() public int size()
...@@ -695,7 +724,7 @@ public class Hashtable extends Dictionary ...@@ -695,7 +724,7 @@ public class Hashtable extends Dictionary
public Iterator iterator() public Iterator iterator()
{ {
return new HashIterator(HashIterator.ENTRIES); return new HashIterator(ENTRIES);
} }
public void clear() public void clear()
...@@ -719,14 +748,15 @@ public class Hashtable extends Dictionary ...@@ -719,14 +748,15 @@ public class Hashtable extends Dictionary
return false; return false;
} }
}; };
// We must specify the correct object to synchronize upon, hence the // We must specify the correct object to synchronize upon, hence the
// use of a non-public API // use of a non-public API
return new Collections.SynchronizedSet(this, r); entries = 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: * As specified by Map, this is:
* <pre> * <pre>
* (o instanceof Map) && entrySet().equals(((Map) o).entrySet()); * (o instanceof Map) && entrySet().equals(((Map) o).entrySet());
...@@ -759,7 +789,7 @@ public class Hashtable extends Dictionary ...@@ -759,7 +789,7 @@ public class Hashtable extends Dictionary
// Since we are already synchronized, and entrySet().iterator() // Since we are already synchronized, and entrySet().iterator()
// would repeatedly re-lock/release the monitor, we directly use the // would repeatedly re-lock/release the monitor, we directly use the
// unsynchronized HashIterator instead. // unsynchronized HashIterator instead.
Iterator itr = new HashIterator(HashIterator.ENTRIES); Iterator itr = new HashIterator(ENTRIES);
int hashcode = 0; int hashcode = 0;
for (int pos = size; pos > 0; pos--) for (int pos = size; pos > 0; pos--)
hashcode += itr.next().hashCode(); hashcode += itr.next().hashCode();
...@@ -782,23 +812,25 @@ public class Hashtable extends Dictionary ...@@ -782,23 +812,25 @@ public class Hashtable extends Dictionary
/** /**
* Helper method for entrySet(), which matches both key and value * Helper method for entrySet(), which matches both key and value
* simultaneously. * simultaneously. Ignores null, as mentioned in entrySet().
* *
* @param o the entry to match * @param o the entry to match
* @return the matching entry, if found, or null * @return the matching entry, if found, or null
* @throws NullPointerException if me.getKey() returns null
* @see #entrySet() * @see #entrySet()
*/ */
private HashEntry getEntry(Object o) 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; return null;
Map.Entry me = (Map.Entry) o;
int idx = hash(me.getKey()); int idx = hash(key);
HashEntry e = buckets[idx]; HashEntry e = buckets[idx];
while (e != null) while (e != null)
{ {
if (e.equals(me)) if (o.equals(e))
return e; return e;
e = e.next; e = e.next;
} }
...@@ -806,6 +838,30 @@ public class Hashtable extends Dictionary ...@@ -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 * 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 * indices; this is called when the addition of a new value would cause
* size() > threshold. Note that the existing Entry objects are reused in * size() > threshold. Note that the existing Entry objects are reused in
...@@ -813,7 +869,8 @@ public class Hashtable extends Dictionary ...@@ -813,7 +869,8 @@ public class Hashtable extends Dictionary
* <p> * <p>
* *
* This is not specified, but the new size is twice the current size plus * 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() protected void rehash()
{ {
...@@ -854,8 +911,8 @@ public class Hashtable extends Dictionary ...@@ -854,8 +911,8 @@ public class Hashtable extends Dictionary
* *
* @param s the stream to write to * @param s the stream to write to
* @throws IOException if the underlying stream fails * @throws IOException if the underlying stream fails
* @serialData the <i>capacity</i>(int) that is the length of the * @serialData the <i>capacity</i> (int) that is the length of the
* bucket array, the <i>size</i>(int) of the hash map * bucket array, the <i>size</i> (int) of the hash map
* are emitted first. They are followed by size entries, * are emitted first. They are followed by size entries,
* each consisting of a key (Object) and a value (Object). * each consisting of a key (Object) and a value (Object).
*/ */
...@@ -870,7 +927,7 @@ public class Hashtable extends Dictionary ...@@ -870,7 +927,7 @@ public class Hashtable extends Dictionary
// Since we are already synchronized, and entrySet().iterator() // Since we are already synchronized, and entrySet().iterator()
// would repeatedly re-lock/release the monitor, we directly use the // would repeatedly re-lock/release the monitor, we directly use the
// unsynchronized HashIterator instead. // unsynchronized HashIterator instead.
Iterator it = new HashIterator(HashIterator.ENTRIES); Iterator it = new HashIterator(ENTRIES);
while (it.hasNext()) while (it.hasNext())
{ {
HashEntry entry = (HashEntry) it.next(); HashEntry entry = (HashEntry) it.next();
...@@ -885,8 +942,8 @@ public class Hashtable extends Dictionary ...@@ -885,8 +942,8 @@ public class Hashtable extends Dictionary
* @param s the stream to read from * @param s the stream to read from
* @throws ClassNotFoundException if the underlying stream fails * @throws ClassNotFoundException if the underlying stream fails
* @throws IOException if the underlying stream fails * @throws IOException if the underlying stream fails
* @serialData the <i>capacity</i>(int) that is the length of the * @serialData the <i>capacity</i> (int) that is the length of the
* bucket array, the <i>size</i>(int) of the hash map * bucket array, the <i>size</i> (int) of the hash map
* are emitted first. They are followed by size entries, * are emitted first. They are followed by size entries,
* each consisting of a key (Object) and a value (Object). * each consisting of a key (Object) and a value (Object).
*/ */
...@@ -901,7 +958,8 @@ public class Hashtable extends Dictionary ...@@ -901,7 +958,8 @@ public class Hashtable extends Dictionary
int len = s.readInt(); int len = s.readInt();
// Read and use key/value pairs. // 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()); put(s.readObject(), s.readObject());
} }
...@@ -916,13 +974,8 @@ public class Hashtable extends Dictionary ...@@ -916,13 +974,8 @@ public class Hashtable extends Dictionary
* *
* @author Jon Zeppieri * @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}, * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
* or {@link #ENTRIES}. * or {@link #ENTRIES}.
...@@ -988,14 +1041,14 @@ public class Hashtable extends Dictionary ...@@ -988,14 +1041,14 @@ public class Hashtable extends Dictionary
last = e; last = e;
if (type == VALUES) if (type == VALUES)
return e.value; return e.value;
else if (type == KEYS) if (type == KEYS)
return e.key; return e.key;
return e; return e;
} }
/** /**
* Removes from the backing Hashtable the last element which was fetched * 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 ConcurrentModificationException if the hashtable was modified
* @throws IllegalStateException if called when there is no last element * @throws IllegalStateException if called when there is no last element
*/ */
...@@ -1007,10 +1060,10 @@ public class Hashtable extends Dictionary ...@@ -1007,10 +1060,10 @@ public class Hashtable extends Dictionary
throw new IllegalStateException(); throw new IllegalStateException();
Hashtable.this.remove(last.key); Hashtable.this.remove(last.key);
knownMod++;
last = null; last = null;
knownMod++;
} }
} } // class HashIterator
/** /**
...@@ -1027,21 +1080,21 @@ public class Hashtable extends Dictionary ...@@ -1027,21 +1080,21 @@ public class Hashtable extends Dictionary
* *
* @author Jon Zeppieri * @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}. * 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. */ /** Current index in the physical hash table. */
int idx; int idx = buckets.length;
/** The last Entry returned by nextEntry(). */ /**
HashEntry last; * Entry which will be returned by the next nextElement() call. It is
/** Entry which will be returned by the next nextElement() call. */ * set if we are iterating through a bucket with multiple entries, or null
* if we must look in the next bucket.
*/
HashEntry next; HashEntry next;
/** /**
...@@ -1051,25 +1104,6 @@ public class Hashtable extends Dictionary ...@@ -1051,25 +1104,6 @@ public class Hashtable extends Dictionary
Enumerator(int type) Enumerator(int type)
{ {
this.type = 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 ...@@ -1078,10 +1112,7 @@ public class Hashtable extends Dictionary
*/ */
public boolean hasMoreElements() public boolean hasMoreElements()
{ {
if (next != null) return count > 0;
return true;
next = nextEntry();
return next != null;
} }
/** /**
...@@ -1091,19 +1122,16 @@ public class Hashtable extends Dictionary ...@@ -1091,19 +1122,16 @@ public class Hashtable extends Dictionary
*/ */
public Object nextElement() public Object nextElement()
{ {
HashEntry e; if (count == 0)
if (next != null)
{
e = next;
next = null;
}
else
e = nextEntry();
if (e == null)
throw new NoSuchElementException("Hashtable Enumerator"); throw new NoSuchElementException("Hashtable Enumerator");
if (type == VALUES) count--;
return e.value; HashEntry e = next;
return e.key;
} while (e == null)
e = buckets[--idx];
next = e.next;
return type == VALUES ? e.value : e.key;
} }
} // class Enumerator
} }
...@@ -31,67 +31,169 @@ import java.io.*; ...@@ -31,67 +31,169 @@ import java.io.*;
/** /**
* This class provides a hashtable-backed implementation of the * This class provides a hashtable-backed implementation of the
* Map interface. Unlike HashMap, it uses object identity to * Map interface, but uses object identity to do its hashing. In fact,
* do its hashing. Also, it uses a linear-probe hash table. * 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 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 * @since 1.4
* @status updated to 1.4
*/ */
public class IdentityHashMap extends AbstractMap public class IdentityHashMap extends AbstractMap
implements Map, Serializable, Cloneable implements Map, Serializable, Cloneable
{ {
/** The default capacity. */
private static final int DEFAULT_CAPACITY = 21; 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.
*/
transient Object[] table;
/**
* The number of structural modifications made so far. Package visible for
* use by nested classes.
*/ */
public IdentityHashMap () 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 * entries. If the number of elements added to this hash map
* exceeds this maximum, the map will grow itself; however, that * exceeds this maximum, the map will grow itself; however, that
* incurs a performance penalty. * 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) 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]; table = new Object[2 * max];
Arrays.fill (table, emptyslot); Arrays.fill(table, emptyslot);
size = 0; // 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. * 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); this(Math.max(m.size() * 2, DEFAULT_CAPACITY));
table = new Object[len]; putAll(m);
Arrays.fill (table, emptyslot);
putAll (m);
} }
public void clear () /**
* Remove all mappings from this map.
*/
public void clear()
{
if (size != 0)
{ {
Arrays.fill (table, emptyslot); modCount++;
Arrays.fill(table, emptyslot);
size = 0; size = 0;
} }
}
/** /**
* Creates a shallow copy where keys and values are not cloned. * Creates a shallow copy where keys and values are not cloned.
*/ */
public Object clone () public Object clone()
{ {
try try
{ {
IdentityHashMap copy = (IdentityHashMap) super.clone (); IdentityHashMap copy = (IdentityHashMap) super.clone();
copy.table = (Object[]) table.clone (); copy.table = (Object[]) table.clone();
copy.entries = null; // invalidate the cache
return copy; return copy;
} }
catch (CloneNotSupportedException e) catch (CloneNotSupportedException e)
...@@ -101,333 +203,716 @@ public class IdentityHashMap extends AbstractMap ...@@ -101,333 +203,716 @@ public class IdentityHashMap extends AbstractMap
} }
} }
public boolean containsKey (Object key) /**
{ * Tests whether the specified key is in this map. Unlike normal Maps,
int h = getHash (key); * this test uses <code>entry == key</code> instead of
int save = h; * <code>entry == null ? key == null : entry.equals(key)</code>.
while (true) *
* @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)
{ {
if (table[h] == key) return key == table[hash(key)];
return true;
if (table[h] == emptyslot)
return false;
h += 2;
if (h >= table.length)
h = 0;
if (h == save)
return false;
}
} }
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) if (table[i] == value)
return true; return true;
return false; 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 () if (entries == null)
entries = new AbstractSet()
{ {
public int size () public int size()
{ {
return size; return size;
} }
public Iterator iterator () public Iterator iterator()
{ {
return new IdentityIterator (IdentityIterator.ENTRIES); return new IdentityIterator(ENTRIES);
} }
public void clear () public void clear()
{ {
IdentityHashMap.this.clear (); IdentityHashMap.this.clear();
} }
public boolean contains (Object o) public boolean contains(Object o)
{ {
if (! (o instanceof Map.Entry)) if (! (o instanceof Map.Entry))
return false; return false;
Map.Entry m = (Map.Entry) o; Map.Entry m = (Map.Entry) o;
return (IdentityHashMap.this.containsKey (m.getKey ()) return m.getValue() == table[hash(m.getKey()) + 1];
&& IdentityHashMap.this.get (m.getKey ()) == m.getValue ());
} }
public boolean remove (Object o) public int hashCode()
{
return IdentityHashMap.this.hashCode();
}
public boolean remove(Object o)
{ {
if (! (o instanceof Map.Entry)) if (! (o instanceof Map.Entry))
return false; return false;
Map.Entry m = (Map.Entry) o; Object key = ((Map.Entry) o).getKey();
if (IdentityHashMap.this.containsKey (m.getKey ()) int h = hash(key);
&& IdentityHashMap.this.get (m.getKey ()) == m.getValue ()) if (table[h] == key)
{ {
int oldsize = size; size--;
IdentityHashMap.this.remove (m.getKey ()); modCount++;
return oldsize != size; table[h] = tombstone;
table[h + 1] = tombstone;
return true;
} }
return false; return false;
} }
}; };
return entries;
} }
public Object get (Object key) /**
* 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)
{ {
int h = getHash (key); // Why did Sun specify this one? The superclass does the right thing.
int save = h; return super.equals(o);
while (true) }
/**
* 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)
{ {
if (table[h] == key) int h = hash(key);
return table[h + 1]; return table[h] == key ? table[h + 1] : null;
if (table[h] == emptyslot)
return null;
h += 2;
if (h >= table.length)
h = 0;
if (h == save)
return null;
} }
/**
* 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 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)
^ 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; 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 () if (keys == null)
keys = new AbstractSet()
{ {
public int size () public int size()
{ {
return size; return size;
} }
public Iterator iterator () public Iterator iterator()
{ {
return new IdentityIterator (IdentityIterator.KEYS); return new IdentityIterator(KEYS);
} }
public void clear () public void clear()
{ {
IdentityHashMap.this.clear (); IdentityHashMap.this.clear();
} }
public boolean contains (Object o) public boolean contains(Object o)
{ {
return IdentityHashMap.this.containsKey (o); return containsKey(o);
} }
public boolean remove (Object o) public int hashCode()
{ {
int oldsize = size; int hash = 0;
IdentityHashMap.this.remove (o); for (int i = table.length - 2; i >= 0; i -= 2)
return oldsize != size; {
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 // Rehash if the load factor is too high.
// -- the division by 2 is implicit on both sides. if (size > threshold)
if (size * 3 > table.length)
{ {
Object[] old = table; Object[] old = table;
table = new Object[old.length * 2]; // This isn't necessarily prime, but it is an odd number of key/value
Arrays.fill (table, emptyslot); // slots, which has a higher probability of fewer collisions.
table = new Object[old.length * 2 + 2];
Arrays.fill(table, emptyslot);
size = 0; size = 0;
for (int i = 0; i < old.length; i += 2) threshold = table.length / 4 * 3;
{
if (old[i] != tombstone && old[i] != emptyslot) for (int i = old.length - 2; i >= 0; i -= 2)
{ {
// Just use put. This isn't very efficient, but it is Object oldkey = old[i];
// ok. if (oldkey != tombstone && oldkey != emptyslot)
put (old[i], old[i + 1]); // Just use put. This isn't very efficient, but it is ok.
} put(oldkey, old[i + 1]);
} }
} }
int h = getHash (key); int h = hash(key);
int save = h;
int del = -1;
while (true)
{
if (table[h] == key) if (table[h] == key)
{ {
Object r = table[h + 1]; Object r = table[h + 1];
table[h + 1] = value; table[h + 1] = value;
return r; 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;
}
if (del != -1) // At this point, we add a new mapping.
{ modCount++;
table[del] = key; size++;
table[del + 1] = value; table[h] = key;
++size; table[h + 1] = value;
return null; return null;
} }
// This is an error. /**
return null; * 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)
{
// Why did Sun specify this one? The superclass does the right thing.
super.putAll(m);
} }
public Object remove (Object key) /**
{ * Removes from the HashMap and returns the value which is mapped by the
int h = getHash (key); * supplied key. If the key maps to nothing, then the HashMap remains
int save = h; * unchanged, and <pre>null</pre> is returned. NOTE: Since the value
while (true) * 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)
{ {
modCount++;
size--;
Object r = table[h + 1]; Object r = table[h + 1];
table[h] = tombstone; table[h] = tombstone;
table[h + 1] = tombstone; table[h + 1] = tombstone;
--size;
return r; return r;
} }
h += 2;
if (h >= table.length)
h = 0;
if (h == save)
break;
}
return null; return null;
} }
public int size () /**
* Returns the number of kay-value mappings currently in this Map
* @return the size
*/
public int size()
{ {
return 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 () if (values == null)
values = new AbstractCollection()
{ {
public int size () public int size()
{ {
return size; return size;
} }
public Iterator iterator () public Iterator iterator()
{ {
return new IdentityIterator (IdentityIterator.VALUES); return new IdentityIterator(VALUES);
} }
public void clear () public void clear()
{ {
IdentityHashMap.this.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;
} }
private class IdentityIterator implements Iterator /**
* 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;
do
{ {
static final int KEYS = 0; if (table[h] == key)
static final int VALUES = 1; return h;
static final int ENTRIES = 2; 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);
// Type of iterator. return del < 0 ? h : del;
int type; }
// Location in the table.
int loc; /**
// How many items we've seen. * This class allows parameterized iteration over IdentityHashMaps. Based
int seen; * 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
{
/**
* 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;
IdentityIterator (int type) /**
* Construct a new Iterator with the supplied type.
* @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
*/
IdentityIterator(int type)
{ {
this.type = 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.
while (true) * @return the next element
* @throws ConcurrentModificationException if the Map was modified
* @throws NoSuchElementException if there is none
*/
public Object next()
{ {
loc += 2; if (knownMod != modCount)
if (loc >= table.length) throw new ConcurrentModificationException();
throw new NoSuchElementException (); if (count == 0)
if (table[loc] != tombstone && table[loc] != emptyslot) throw new NoSuchElementException();
count--;
Object key;
do
{ {
++seen; loc -= 2;
return table[loc]; 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
if (loc >= table.length * with the <pre>next()</pre> method.
|| table[loc] == tombstone * @throws ConcurrentModificationException if the Map was modified
|| table[loc] == emptyslot) * @throws IllegalStateException if called when there is no last element
throw new IllegalStateException (); */
public void remove()
{
if (knownMod != modCount)
throw new ConcurrentModificationException();
if (loc == table.length || table[loc] == tombstone)
throw new IllegalStateException();
modCount++;
size--;
table[loc] = tombstone; table[loc] = tombstone;
table[loc + 1] = tombstone; table[loc + 1] = tombstone;
--size; knownMod++;
}
} }
} // class IdentityIterator
private void readObject (ObjectInputStream s) /**
throws IOException, ClassNotFoundException * 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
{ {
int num = s.readInt (); /** The location of this entry. */
for (int i = 0; i < num; ++i) 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)
{ {
Object key = s.readObject (); this.loc = loc;
Object value = s.readObject ();
put (key, value);
} }
/**
* 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();
} }
private void writeObject (ObjectOutputStream s) /**
throws IOException * 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()
{ {
s.writeInt (size); if (knownMod != modCount || table[loc] == tombstone)
Iterator it = entrySet ().iterator (); throw new ConcurrentModificationException();
while (it.hasNext ()) 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()
{ {
Map.Entry entry = (Map.Entry) it.next (); if (knownMod != modCount || table[loc] == tombstone)
s.writeObject (entry.getKey ()); throw new ConcurrentModificationException();
s.writeObject (entry.getValue ()); 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]));
} }
// Compute the hash value we will use for an object. /**
private int getHash (Object o) * 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)
{ {
return 2 * Math.abs (System.identityHashCode (o) % (table.length / 2)); if (knownMod != modCount || table[loc] == tombstone)
throw new ConcurrentModificationException();
Object r = table[loc + 1];
table[loc + 1] = value;
return r;
} }
// Number of items in hash table. /**
private int size; * This provides a string representation of the entry. It is of the form
// The table itself. * "key=value", where string concatenation is used on key and value.
private Object[] table; *
* @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
// This object is used to mark deleted items. /**
private static final Object tombstone = new Object (); * Reads the object from a serial stream.
// This object is used to mark empty slots. We need this because *
// using null is ambiguous. * @param s the stream to read from
private static final Object emptyslot = new Object (); * @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
{
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());
}
/**
* 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.defaultWriteObject();
s.writeInt(size);
for (int i = table.length - 2; i >= 0; i -= 2)
{
Object key = table[i];
if (key != tombstone && key != emptyslot)
{
s.writeObject(key);
s.writeObject(table[i + 1]);
}
}
}
} }
...@@ -28,11 +28,6 @@ executable file might be covered by the GNU General Public License. */ ...@@ -28,11 +28,6 @@ executable file might be covered by the GNU General Public License. */
package java.util; 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 * This class provides a hashtable-backed implementation of the
* Map interface, with predictable traversal order. * Map interface, with predictable traversal order.
...@@ -89,6 +84,7 @@ import java.io.ObjectOutputStream; ...@@ -89,6 +84,7 @@ import java.io.ObjectOutputStream;
* @see TreeMap * @see TreeMap
* @see Hashtable * @see Hashtable
* @since 1.4 * @since 1.4
* @status updated to 1.4
*/ */
public class LinkedHashMap extends HashMap public class LinkedHashMap extends HashMap
{ {
...@@ -218,8 +214,8 @@ public class LinkedHashMap extends HashMap ...@@ -218,8 +214,8 @@ public class LinkedHashMap extends HashMap
* Construct a new insertion-ordered LinkedHashMap with a specific * Construct a new insertion-ordered LinkedHashMap with a specific
* inital capacity and default load factor of 0.75. * inital capacity and default load factor of 0.75.
* *
* @param initialCapacity the initial capacity of this HashMap (>=0) * @param initialCapacity the initial capacity of this HashMap (&gt;= 0)
* @throws IllegalArgumentException if (initialCapacity < 0) * @throws IllegalArgumentException if (initialCapacity &lt; 0)
*/ */
public LinkedHashMap(int initialCapacity) public LinkedHashMap(int initialCapacity)
{ {
...@@ -231,10 +227,10 @@ public class LinkedHashMap extends HashMap ...@@ -231,10 +227,10 @@ public class LinkedHashMap extends HashMap
* Construct a new insertion-orderd LinkedHashMap with a specific * Construct a new insertion-orderd LinkedHashMap with a specific
* inital capacity and load factor. * inital capacity and load factor.
* *
* @param initialCapacity the initial capacity (>=0) * @param initialCapacity the initial capacity (&gt;= 0)
* @param loadFactor the load factor (>0, not NaN) * @param loadFactor the load factor (&gt; 0, not NaN)
* @throws IllegalArgumentException if (initialCapacity < 0) || * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
* ! (loadFactor > 0.0) * ! (loadFactor &gt; 0.0)
*/ */
public LinkedHashMap(int initialCapacity, float loadFactor) public LinkedHashMap(int initialCapacity, float loadFactor)
{ {
...@@ -281,7 +277,7 @@ public class LinkedHashMap extends HashMap ...@@ -281,7 +277,7 @@ public class LinkedHashMap extends HashMap
LinkedHashEntry e = head; LinkedHashEntry e = head;
while (e != null) while (e != null)
{ {
if (value == null ? e.value == null : value.equals(e.value)) if (equals(value, e.value))
return true; return true;
e = e.succ; e = e.succ;
} }
...@@ -307,7 +303,7 @@ public class LinkedHashMap extends HashMap ...@@ -307,7 +303,7 @@ public class LinkedHashMap extends HashMap
HashEntry e = buckets[idx]; HashEntry e = buckets[idx];
while (e != null) while (e != null)
{ {
if (key == null ? e.key == null : key.equals(e.key)) if (equals(key, e.key))
{ {
if (accessOrder) if (accessOrder)
{ {
...@@ -376,13 +372,14 @@ public class LinkedHashMap extends HashMap ...@@ -376,13 +372,14 @@ public class LinkedHashMap extends HashMap
return false; 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). * new Entry, followed by performing bookkeeping (like removeEldestEntry).
* *
* @param key the key of the new Entry * @param key the key of the new Entry
* @param value the value * @param value the value
* @param idx the index in buckets where the new Entry belongs * @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 #put(Object, Object)
* @see #removeEldestEntry(Map.Entry) * @see #removeEldestEntry(Map.Entry)
*/ */
...@@ -397,6 +394,11 @@ public class LinkedHashMap extends HashMap ...@@ -397,6 +394,11 @@ public class LinkedHashMap extends HashMap
remove(head); 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) void putAllInternal(Map m)
{ {
head = null; head = null;
...@@ -466,8 +468,8 @@ public class LinkedHashMap extends HashMap ...@@ -466,8 +468,8 @@ public class LinkedHashMap extends HashMap
throw new IllegalStateException(); throw new IllegalStateException();
LinkedHashMap.this.remove(last.key); LinkedHashMap.this.remove(last.key);
knownMod++;
last = null; last = null;
knownMod++;
} }
}; };
} }
......
/* 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);
}
}
/* LinkedList.java -- Linked list implementation of the List interface /* 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. This file is part of GNU Classpath.
...@@ -32,25 +32,50 @@ import java.io.ObjectInputStream; ...@@ -32,25 +32,50 @@ import java.io.ObjectInputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Array; 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 public class LinkedList extends AbstractSequentialList
implements List, Cloneable, Serializable 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 * The first element in the list.
* 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.
*/ */
transient Entry first; transient Entry first;
/**
* The last element in the list.
*/
transient Entry last; transient Entry last;
/** /**
...@@ -61,17 +86,26 @@ public class LinkedList extends AbstractSequentialList ...@@ -61,17 +86,26 @@ public class LinkedList extends AbstractSequentialList
/** /**
* Class to represent an entry in the list. Holds a single element. * 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; Object data;
/** The next list entry, null if this is last. */
Entry next; Entry next;
/** The previous list entry, null if this is first. */
Entry previous; Entry previous;
/**
* Construct an entry.
* @param data the list element
*/
Entry(Object data) Entry(Object data)
{ {
this.data = data; this.data = data;
} }
} } // class Entry
/** /**
* Obtain the Entry at a given position in a list. This method of course * Obtain the Entry at a given position in a list. This method of course
...@@ -82,7 +116,8 @@ public class LinkedList extends AbstractSequentialList ...@@ -82,7 +116,8 @@ public class LinkedList extends AbstractSequentialList
* For speed and flexibility, range checking is not done in this method: * For speed and flexibility, range checking is not done in this method:
* Incorrect values will be returned if (n < 0) or (n >= size). * 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) private Entry getEntry(int n)
{ {
...@@ -92,28 +127,29 @@ public class LinkedList extends AbstractSequentialList ...@@ -92,28 +127,29 @@ public class LinkedList extends AbstractSequentialList
e = first; e = first;
// n less than size/2, iterate from start // n less than size/2, iterate from start
while (n-- > 0) while (n-- > 0)
{
e = e.next; e = e.next;
} }
}
else else
{ {
e = last; e = last;
// n greater than size/2, iterate from end // n greater than size/2, iterate from end
while (++n < size) while (++n < size)
{
e = e.previous; e = e.previous;
} }
}
return e; 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 * Remove an entry from the list. This will adjust size and deal with
* the responsibility of the caller. */ * `first' and `last' appropriatly.
*
* @param e the entry to remove
*/
private void removeEntry(Entry e) private void removeEntry(Entry e)
{ {
if (size == 1) modCount++;
size--;
if (size == 0)
first = last = null; first = last = null;
else else
{ {
...@@ -133,7 +169,32 @@ public class LinkedList extends AbstractSequentialList ...@@ -133,7 +169,32 @@ public class LinkedList extends AbstractSequentialList
e.previous.next = e.next; 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 ...@@ -141,24 +202,26 @@ public class LinkedList extends AbstractSequentialList
*/ */
public LinkedList() public LinkedList()
{ {
super();
} }
/** /**
* Create a linked list containing the elements, in order, of a given * Create a linked list containing the elements, in order, of a given
* collection. * 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) 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); addAll(c);
} }
/**
* Returns the first element in the list.
*
* @return the first list element
* @throws NoSuchElementException if the list is empty
*/
public Object getFirst() public Object getFirst()
{ {
if (size == 0) if (size == 0)
...@@ -166,6 +229,12 @@ public class LinkedList extends AbstractSequentialList ...@@ -166,6 +229,12 @@ public class LinkedList extends AbstractSequentialList
return first.data; 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() public Object getLast()
{ {
if (size == 0) if (size == 0)
...@@ -173,12 +242,18 @@ public class LinkedList extends AbstractSequentialList ...@@ -173,12 +242,18 @@ public class LinkedList extends AbstractSequentialList
return last.data; 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() public Object removeFirst()
{ {
if (size == 0) if (size == 0)
throw new NoSuchElementException(); throw new NoSuchElementException();
size--;
modCount++; modCount++;
size--;
Object r = first.data; Object r = first.data;
if (first.next != null) if (first.next != null)
...@@ -191,12 +266,18 @@ public class LinkedList extends AbstractSequentialList ...@@ -191,12 +266,18 @@ public class LinkedList extends AbstractSequentialList
return r; 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() public Object removeLast()
{ {
if (size == 0) if (size == 0)
throw new NoSuchElementException(); throw new NoSuchElementException();
size--;
modCount++; modCount++;
size--;
Object r = last.data; Object r = last.data;
if (last.previous != null) if (last.previous != null)
...@@ -209,11 +290,16 @@ public class LinkedList extends AbstractSequentialList ...@@ -209,11 +290,16 @@ public class LinkedList extends AbstractSequentialList
return r; return r;
} }
/**
* Insert an element at the first of the list.
*
* @param o the element to insert
*/
public void addFirst(Object o) public void addFirst(Object o)
{ {
modCount++;
Entry e = new Entry(o); Entry e = new Entry(o);
modCount++;
if (size == 0) if (size == 0)
first = last = e; first = last = e;
else else
...@@ -225,14 +311,24 @@ public class LinkedList extends AbstractSequentialList ...@@ -225,14 +311,24 @@ public class LinkedList extends AbstractSequentialList
size++; size++;
} }
/**
* Insert an element at the last of the list.
*
* @param o the element to insert
*/
public void addLast(Object o) public void addLast(Object o)
{ {
modCount++;
addLastEntry(new Entry(o)); addLastEntry(new Entry(o));
} }
/**
* Inserts an element at the end of the list.
*
* @param e the entry to add
*/
private void addLastEntry(Entry e) private void addLastEntry(Entry e)
{ {
modCount++;
if (size == 0) if (size == 0)
first = last = e; first = last = e;
else else
...@@ -244,37 +340,60 @@ public class LinkedList extends AbstractSequentialList ...@@ -244,37 +340,60 @@ public class LinkedList extends AbstractSequentialList
size++; 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) public boolean contains(Object o)
{ {
Entry e = first; Entry e = first;
while (e != null) while (e != null)
{ {
if (e.data == null ? o == null : o.equals(e.data)) if (equals(o, e.data))
return true; return true;
e = e.next; e = e.next;
} }
return false; return false;
} }
/**
* Returns the size of the list.
*
* @return the list size
*/
public int size() public int size()
{ {
return 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) public boolean add(Object o)
{ {
modCount++;
addLastEntry(new Entry(o)); addLastEntry(new Entry(o));
return true; 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) public boolean remove(Object o)
{ {
modCount++;
Entry e = first; Entry e = first;
while (e != null) while (e != null)
{ {
if (e.data == null ? o == null : o.equals(e.data)) if (equals(o, e.data))
{ {
removeEntry(e); removeEntry(e);
return true; return true;
...@@ -284,17 +403,33 @@ public class LinkedList extends AbstractSequentialList ...@@ -284,17 +403,33 @@ public class LinkedList extends AbstractSequentialList
return false; 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) public boolean addAll(Collection c)
{ {
return addAll(size, 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) public boolean addAll(int index, Collection c)
{ {
if (index < 0 || index > size) checkBoundsInclusive(index);
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size);
modCount++;
int csize = c.size(); int csize = c.size();
if (csize == 0) if (csize == 0)
...@@ -303,8 +438,8 @@ public class LinkedList extends AbstractSequentialList ...@@ -303,8 +438,8 @@ public class LinkedList extends AbstractSequentialList
Iterator itr = c.iterator(); Iterator itr = c.iterator();
// Get the entries just before and after index. If index is at the start // 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 // of the list, BEFORE is null. If index is at the end of the list, AFTER
// null. If the list is empty, both are null. // is null. If the list is empty, both are null.
Entry after = null; Entry after = null;
Entry before = null; Entry before = null;
if (index != size) if (index != size)
...@@ -332,7 +467,10 @@ public class LinkedList extends AbstractSequentialList ...@@ -332,7 +467,10 @@ public class LinkedList extends AbstractSequentialList
prev.next = e; prev.next = e;
prev = e; prev = e;
} }
// Link the new chain of entries into the list. // Link the new chain of entries into the list.
modCount++;
size += csize;
prev.next = after; prev.next = after;
if (after != null) if (after != null)
after.previous = e; after.previous = e;
...@@ -343,52 +481,68 @@ public class LinkedList extends AbstractSequentialList ...@@ -343,52 +481,68 @@ public class LinkedList extends AbstractSequentialList
before.next = firstNew; before.next = firstNew;
else else
first = firstNew; first = firstNew;
size += csize;
return true; return true;
} }
/**
* Remove all elements from this list.
*/
public void clear() public void clear()
{ {
if (size > 0)
{
modCount++; modCount++;
first = null; first = null;
last = null; last = null;
size = 0; 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) public Object get(int index)
{ {
if (index < 0 || index >= size) checkBoundsExclusive(index);
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + return getEntry(index).data;
size);
Entry e = getEntry(index);
return e.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) public Object set(int index, Object o)
{ {
if (index < 0 || index >= size) checkBoundsExclusive(index);
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size);
Entry e = getEntry(index); Entry e = getEntry(index);
Object old = e.data; Object old = e.data;
e.data = o; e.data = o;
return old; 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) public void add(int index, Object o)
{ {
if (index < 0 || index > size) checkBoundsInclusive(index);
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + Entry e = new Entry(o);
size);
modCount++;
addEntry(index, new Entry(o));
}
private void addEntry(int index, Entry e)
{
if (index < size) if (index < size)
{ {
modCount++;
Entry after = getEntry(index); Entry after = getEntry(index);
e.next = after; e.next = after;
e.previous = after.previous; e.previous = after.previous;
...@@ -403,40 +557,56 @@ public class LinkedList extends AbstractSequentialList ...@@ -403,40 +557,56 @@ public class LinkedList extends AbstractSequentialList
addLastEntry(e); 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) public Object remove(int index)
{ {
if (index < 0 || index >= size) checkBoundsExclusive(index);
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size);
modCount++;
Entry e = getEntry(index); Entry e = getEntry(index);
removeEntry(e); removeEntry(e);
return e.data; 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) public int indexOf(Object o)
{ {
int index = 0; int index = 0;
Entry e = first; Entry e = first;
while (e != null) while (e != null)
{ {
if (e.data == null ? o == null : o.equals(e.data)) if (equals(o, e.data))
return index; return index;
++index; index++;
e = e.next; e = e.next;
} }
return -1; 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) public int lastIndexOf(Object o)
{ {
int index = size - 1; int index = size - 1;
Entry e = last; Entry e = last;
while (e != null) while (e != null)
{ {
if (e.data == null ? o == null : o.equals(e.data)) if (equals(o, e.data))
return index; return index;
--index; index--;
e = e.previous; e = e.previous;
} }
return -1; return -1;
...@@ -448,21 +618,20 @@ public class LinkedList extends AbstractSequentialList ...@@ -448,21 +618,20 @@ public class LinkedList extends AbstractSequentialList
* methods. * methods.
* *
* @param index the index of the element to be returned by the first call to * @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. * next(), or size() to be initially positioned at the end of the list
* @exception IndexOutOfBoundsException if index < 0 || index > size(). * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
*/ */
public ListIterator listIterator(int index) public ListIterator listIterator(int index)
{ {
if (index < 0 || index > size) checkBoundsInclusive(index);
throw new IndexOutOfBoundsException("Index: " + index + ", Size:" +
size);
return new LinkedListItr(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 * @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() public Object clone()
{ {
...@@ -479,6 +648,11 @@ public class LinkedList extends AbstractSequentialList ...@@ -479,6 +648,11 @@ public class LinkedList extends AbstractSequentialList
return copy; return copy;
} }
/**
* Returns an array which contains the elements of the list in order.
*
* @return an array containing the list elements
*/
public Object[] toArray() public Object[] toArray()
{ {
Object[] array = new Object[size]; Object[] array = new Object[size];
...@@ -491,59 +665,102 @@ public class LinkedList extends AbstractSequentialList ...@@ -491,59 +665,102 @@ public class LinkedList extends AbstractSequentialList
return array; 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) if (a.length < size)
array = (Object[]) Array.newInstance(array.getClass().getComponentType(), a = (Object[]) Array.newInstance(a.getClass().getComponentType(), size);
size); else if (a.length > size)
else if (array.length > size) a[size] = null;
array[size] = null;
Entry e = first; Entry e = first;
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
array[i] = e.data; a[i] = e.data;
e = e.next; e = e.next;
} }
return array; return a;
} }
/** /**
* Serialize an object to a stream. * Serializes this object to the given stream.
* @serialdata the size of the list (int), followed by all the elements *
* (Object) in proper order. * @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 private void writeObject(ObjectOutputStream s) throws IOException
{ {
s.defaultWriteObject();
s.writeInt(size); s.writeInt(size);
Iterator itr = iterator(); Entry e = first;
for (int i = 0; i < size; i++) while (e != null)
s.writeObject(itr.next()); {
s.writeObject(e.data);
e = e.next;
}
} }
/** /**
* Deserialize an object from a stream. * Deserializes this object from the given stream.
* @serialdata the size of the list (int), followed by all the elements *
* (Object) in proper order. * @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) private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException throws IOException, ClassNotFoundException
{ {
int serialSize = s.readInt(); s.defaultReadObject();
for (int i = 0; i < serialSize; i++) int i = s.readInt();
while (--i >= 0)
addLastEntry(new Entry(s.readObject())); 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. * 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; /** Number of modifications we know about. */
Entry next; // entry that will be returned by next(). private int knownMod = modCount;
Entry previous; // entry that will be returned by previous().
Entry lastReturned; // entry that will be affected by remove() or set(). /** Entry that will be returned by next(). */
int position; // index of `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) LinkedListItr(int index)
{ {
if (index == size) if (index == size)
...@@ -557,39 +774,74 @@ public class LinkedList extends AbstractSequentialList ...@@ -557,39 +774,74 @@ public class LinkedList extends AbstractSequentialList
previous = next.previous; previous = next.previous;
} }
position = index; position = index;
knownMod = modCount;
} }
/**
* Checks for iterator consistency.
*
* @throws ConcurrentModificationException if the list was modified
*/
private void checkMod() private void checkMod()
{ {
if (knownMod != modCount) 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() public int nextIndex()
{ {
checkMod(); checkMod();
return position; return position;
} }
/**
* Returns the index of the previous element.
*
* @return the previous index
* @throws ConcurrentModificationException if the list was modified
*/
public int previousIndex() public int previousIndex()
{ {
checkMod(); checkMod();
return position - 1; 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() public boolean hasNext()
{ {
checkMod(); checkMod();
return (next != null); 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() public boolean hasPrevious()
{ {
checkMod(); checkMod();
return (previous != null); 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() public Object next()
{ {
checkMod(); checkMod();
...@@ -601,6 +853,13 @@ public class LinkedList extends AbstractSequentialList ...@@ -601,6 +853,13 @@ public class LinkedList extends AbstractSequentialList
return lastReturned.data; 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() public Object previous()
{ {
checkMod(); checkMod();
...@@ -612,6 +871,12 @@ public class LinkedList extends AbstractSequentialList ...@@ -612,6 +871,12 @@ public class LinkedList extends AbstractSequentialList
return lastReturned.data; 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() public void remove()
{ {
checkMod(); checkMod();
...@@ -625,18 +890,25 @@ public class LinkedList extends AbstractSequentialList ...@@ -625,18 +890,25 @@ public class LinkedList extends AbstractSequentialList
next = lastReturned.next; next = lastReturned.next;
previous = lastReturned.previous; previous = lastReturned.previous;
modCount++;
knownMod++;
removeEntry(lastReturned); removeEntry(lastReturned);
knownMod++;
lastReturned = null; 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) public void add(Object o)
{ {
checkMod(); checkMod();
modCount++; modCount++;
knownMod++; knownMod++;
size++;
position++;
Entry e = new Entry(o); Entry e = new Entry(o);
e.previous = previous; e.previous = previous;
e.next = next; e.next = next;
...@@ -647,19 +919,21 @@ public class LinkedList extends AbstractSequentialList ...@@ -647,19 +919,21 @@ public class LinkedList extends AbstractSequentialList
first = e; first = e;
if (next != null) if (next != null)
{
next.previous = e; next.previous = e;
next = next.next;
}
else else
last = e; last = e;
previous = e; previous = e;
size++;
position++;
lastReturned = null; 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) public void set(Object o)
{ {
checkMod(); checkMod();
......
...@@ -190,7 +190,7 @@ public interface List extends Collection ...@@ -190,7 +190,7 @@ public interface List extends Collection
* @see Object#equals(Object) * @see Object#equals(Object)
* @see #hashCode() * @see #hashCode()
*/ */
boolean equals(Object o); /* boolean equals(Object o);*/
/** /**
* Get the element at a given index in this list. * Get the element at a given index in this list.
...@@ -288,7 +288,7 @@ public interface List extends Collection ...@@ -288,7 +288,7 @@ public interface List extends Collection
Object remove(int index); 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 * operation). That is, remove the first element e such that
* <code>o == null ? e == null : o.equals(e)</code>. * <code>o == null ? e == null : o.equals(e)</code>.
* *
......
...@@ -32,24 +32,30 @@ package java.util; ...@@ -32,24 +32,30 @@ package java.util;
* "The Java Language Specification", ISBN 0-201-63451-1 * "The Java Language Specification", ISBN 0-201-63451-1
* plus online API docs for JDK 1.2 beta from http://www.javasoft.com. * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct * Status: Believed complete and correct
*/
/** /**
* Stack provides a Last In First Out (LIFO) data type, commonly known * Stack provides a Last In First Out (LIFO) data type, commonly known
* as a Stack. * as a Stack. Stack itself extends Vector and provides the additional
* * methods for stack manipulation (push, pop, peek). You can also seek for
* Stack itself extends Vector and provides the additional methods * the 1-based position of an element on the stack.
* for stack manipulation (push, pop, peek).
* *
* @author Warren Levy <warrenl@cygnus.com> * @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 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 // but have used Vector fields directly for efficiency (i.e. this
// often reduces out duplicate bounds checking). // often reduces out duplicate bounds checking).
/**
* Compatible with JDK 1.0+.
*/
private static final long serialVersionUID = 1224463164541339165L; private static final long serialVersionUID = 1224463164541339165L;
/** /**
...@@ -57,16 +63,15 @@ public class Stack extends Vector ...@@ -57,16 +63,15 @@ public class Stack extends Vector
*/ */
public Stack() public Stack()
{ {
super();
} }
/** /**
* Pushes an Object onto the top of the stack. This method is effectively * 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 * @param item the Object to push onto the stack
* @returns the Object pushed onto the stack * @return the Object pushed onto the stack
* @see java.util.Vector#addElement(java.util.Object) * @see Vector#addElement(Object)
*/ */
public Object push(Object item) public Object push(Object item)
{ {
...@@ -80,26 +85,29 @@ public class Stack extends Vector ...@@ -80,26 +85,29 @@ public class Stack extends Vector
/** /**
* Pops an item from the stack and returns it. The item popped is * 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() public synchronized Object pop()
{ {
if (elementCount == 0) if (elementCount == 0)
throw new EmptyStackException(); throw new EmptyStackException();
modCount++;
Object obj = elementData[--elementCount]; 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; elementData[elementCount] = null;
return obj; 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() public synchronized Object peek()
{ {
...@@ -110,11 +118,11 @@ public class Stack extends Vector ...@@ -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; return elementCount == 0;
} }
...@@ -122,18 +130,18 @@ public class Stack extends Vector ...@@ -122,18 +130,18 @@ public class Stack extends Vector
/** /**
* Returns the position of an Object on the stack, with the top * Returns the position of an Object on the stack, with the top
* most Object being at position 1, and each Object deeper in the * 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 * @param o The object to search for
* @returns The 1 based depth of the Object, or -1 if the Object * @return The 1 based depth of the Object, or -1 if the Object
* is not on the stack. * is not on the stack
*/ */
public synchronized int search(Object o) public synchronized int search(Object o)
{ {
for (int i = elementCount-1; i >=0; --i) int i = elementCount;
if (elementData[i].equals(o)) while (--i >= 0)
if (equals(o, elementData[i]))
return elementCount - i; return elementCount - i;
return -1; return -1;
} }
} }
...@@ -38,80 +38,166 @@ import java.io.IOException; ...@@ -38,80 +38,166 @@ import java.io.IOException;
* interface. Elements in the Map will be sorted by either a user-provided * interface. Elements in the Map will be sorted by either a user-provided
* Comparator object, or by the natural ordering of the keys. * Comparator object, or by the natural ordering of the keys.
* *
* The algorithms are adopted from Corman, Leiserson, * The algorithms are adopted from Corman, Leiserson, and Rivest's
* and Rivest's <i>Introduction to Algorithms.</i> In other words, * <i>Introduction to Algorithms.</i> TreeMap guarantees O(log n)
* I cribbed from the same pseudocode as Sun. <em>Any similarity * insertion and deletion of elements. That being said, there is a large
* between my code and Sun's (if there is any -- I have never looked * enough constant coefficient in front of that "log n" (overhead involved
* at Sun's) is a result of this fact.</em> * 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
* TreeMap guarantees O(log n) insertion and deletion of elements. That * just use a LinkedHashMap to maintain the order while providing O(1) access.
* 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.
* *
* TreeMap is a part of the JDK1.2 Collections API. Null keys are allowed * 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 * only if a Comparator is used which can deal with them; natural ordering
* always allowed. * 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 Jon Zeppieri
* @author Bryce McKinlay * @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 public class TreeMap extends AbstractMap
implements SortedMap, Cloneable, Serializable implements SortedMap, Cloneable, Serializable
{ {
private static final int RED = -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).
/**
* Compatible with JDK 1.2.
*/
private static final long serialVersionUID = 919286545866124006L;
/**
* Color status of a node. Package visible for use by nested classes.
*/
static final int RED = -1,
BLACK = 1; BLACK = 1;
/** 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 * Sentinal node, used to avoid null checks for corner cases and make the
to thread-safety concerns. */ * delete rebalance code simpler. The rebalance code must never assign
transient Node nil = new Node(null, null); * 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;
}
/** The root node of this TreeMap */ /**
transient Node root = nil; * The root node of this TreeMap.
*/
private transient Node root = nil;
/** The size of this TreeMap */ /**
transient int size = 0; * The size of this TreeMap. Package visible for use by nested classes.
*/
transient int size;
/** Number of modifications */ /**
transient int modCount = 0; * 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; 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); super(key, value);
this.color = BLACK; this.color = color;
} }
} }
/** /**
* Instantiate a new TreeMap with no elements, using the keys' * Instantiate a new TreeMap with no elements, using the keys' natural
* natural ordering to sort. * 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() public TreeMap()
{ {
this((Comparator) null);
} }
/** /**
* Instantiate a new TreeMap with no elements, using the provided * Instantiate a new TreeMap with no elements, using the provided comparator
* comparator to sort. * 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 * @param comparator the sort order for the keys of this map, or null
* the keys of this SortedMap * for the natural order
*/ */
public TreeMap(Comparator c) public TreeMap(Comparator c)
{ {
...@@ -119,40 +205,43 @@ public class TreeMap extends AbstractMap ...@@ -119,40 +205,43 @@ public class TreeMap extends AbstractMap
} }
/** /**
* Instantiate a new TreeMap, initializing it with all of the * Instantiate a new TreeMap, initializing it with all of the elements in
* elements in the provided Map. The elements will be sorted * the provided Map. The elements will be sorted using the natural
* using the natural ordering of the keys. * 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
* @param map a Map, whose keys will be put into * comparable, otherwise map operations may throw a
* this TreeMap * {@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) public TreeMap(Map map)
{ {
this((Comparator) null);
putAll(map); putAll(map);
} }
/** /**
* Instantiate a new TreeMap, initializing it with all of the * Instantiate a new TreeMap, initializing it with all of the elements in
* elements in the provided SortedMap. The elements will be sorted * the provided SortedMap. The elements will be sorted using the same
* using the same method as in the provided SortedMap. * 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) public TreeMap(SortedMap sm)
{ {
this(sm.comparator()); this(sm.comparator());
int pos = sm.size();
int sm_size = sm.size();
Iterator itr = sm.entrySet().iterator(); Iterator itr = sm.entrySet().iterator();
fabricateTree(sm_size); fabricateTree(pos);
Node node = firstNode(); Node node = firstNode();
for (int i = 0; i < sm_size; i++) while (--pos >= 0)
{ {
Map.Entry me = (Map.Entry) itr.next(); Map.Entry me = (Map.Entry) itr.next();
node.key = me.getKey(); node.key = me.getKey();
...@@ -161,20 +250,25 @@ public class TreeMap extends AbstractMap ...@@ -161,20 +250,25 @@ public class TreeMap extends AbstractMap
} }
} }
public int size() /**
{ * Clears the Map so it has no keys. This is O(1).
return size; */
}
public void clear() public void clear()
{ {
if (size > 0)
{
modCount++; modCount++;
root = nil; root = nil;
// nil node could have a residual parent reference, clear it for GC.
nil.parent = null;
size = 0; 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() public Object clone()
{ {
TreeMap copy = null; TreeMap copy = null;
...@@ -185,8 +279,7 @@ public class TreeMap extends AbstractMap ...@@ -185,8 +279,7 @@ public class TreeMap extends AbstractMap
catch (CloneNotSupportedException x) catch (CloneNotSupportedException x)
{ {
} }
// Each instance must have a unique sentinal. copy.entries = null;
copy.nil = new Node(null, null);
copy.fabricateTree(size); copy.fabricateTree(size);
Node node = firstNode(); Node node = firstNode();
...@@ -202,38 +295,69 @@ public class TreeMap extends AbstractMap ...@@ -202,38 +295,69 @@ public class TreeMap extends AbstractMap
return copy; 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() public Comparator comparator()
{ {
return 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) public boolean containsKey(Object key)
{ {
return getNode(key) != nil; 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) public boolean containsValue(Object value)
{ {
Node node = firstNode(); Node node = firstNode();
Object currentVal;
while (node != nil) while (node != nil)
{ {
currentVal = node.getValue(); if (equals(value, node.value))
if (value == null ? currentVal == null : value.equals (currentVal))
return true; return true;
node = successor(node); node = successor(node);
} }
return false; 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() public Set entrySet()
{ {
// Create an AbstractSet with custom implementations of those methods that if (entries == null)
// can be overriden easily and efficiently. // Create an AbstractSet with custom implementations of those methods
return new AbstractSet() // that can be overriden easily and efficiently.
entries = new AbstractSet()
{ {
public int size() public int size()
{ {
...@@ -242,7 +366,7 @@ public class TreeMap extends AbstractMap ...@@ -242,7 +366,7 @@ public class TreeMap extends AbstractMap
public Iterator iterator() public Iterator iterator()
{ {
return new TreeIterator(TreeIterator.ENTRIES); return new TreeIterator(ENTRIES);
} }
public void clear() public void clear()
...@@ -252,20 +376,20 @@ public class TreeMap extends AbstractMap ...@@ -252,20 +376,20 @@ public class TreeMap extends AbstractMap
public boolean contains(Object o) public boolean contains(Object o)
{ {
if (!(o instanceof Map.Entry)) if (! (o instanceof Map.Entry))
return false; return false;
Map.Entry me = (Map.Entry) o; Map.Entry me = (Map.Entry) o;
Node n = getNode(me.getKey()); Node n = getNode(me.getKey());
return (n != nil && me.getValue().equals(n.value)); return n != nil && AbstractSet.equals(me.getValue(), n.value);
} }
public boolean remove(Object o) public boolean remove(Object o)
{ {
if (!(o instanceof Map.Entry)) if (! (o instanceof Map.Entry))
return false; return false;
Map.Entry me = (Map.Entry) o; Map.Entry me = (Map.Entry) o;
Node n = getNode(me.getKey()); Node n = getNode(me.getKey());
if (n != nil && me.getValue().equals(n.value)) if (n != nil && AbstractSet.equals(me.getValue(), n.value))
{ {
removeNode(n); removeNode(n);
return true; return true;
...@@ -273,72 +397,77 @@ public class TreeMap extends AbstractMap ...@@ -273,72 +397,77 @@ public class TreeMap extends AbstractMap
return false; 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() public Object firstKey()
{ {
if (root == nil) if (root == nil)
throw new NoSuchElementException("empty"); throw new NoSuchElementException();
return firstNode().getKey(); return firstNode().key;
}
private Node firstNode()
{
if (root == nil)
return nil;
Node node = root;
while (node.left != nil)
node = node.left;
return node;
}
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) public Object get(Object key)
{ {
// Exploit fact that nil.value == null.
return getNode(key).value; return getNode(key).value;
} }
/** Return the TreeMap.Node associated with KEY, or the nil node if no such /**
node exists in the tree. */ * Returns a view of this Map including all entries with keys less than
private Node getNode(Object key) * <code>toKey</code>. The returned map is backed by the original, so changes
{ * in one appear in the other. The submap will throw an
int comparison; * {@link IllegalArgumentException} for any attempt to access or add an
Node current = root; * element beyond the specified cutoff. The returned map does not include
* the endpoint; if you want inclusion, pass the successor element.
while (current != nil) *
* @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)
{ {
comparison = compare(key, current.key); return new SubMap(nil, toKey);
if (comparison > 0)
current = current.right;
else if (comparison < 0)
current = current.left;
else
return current;
}
return current;
} }
/**
* 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() public Set keySet()
{ {
// Create an AbstractSet with custom implementations of those methods that if (keys == null)
// can be overriden easily and efficiently. // Create an AbstractSet with custom implementations of those methods
return new AbstractSet() // that can be overriden easily and efficiently.
keys = new AbstractSet()
{ {
public int size() public int size()
{ {
...@@ -347,7 +476,7 @@ public class TreeMap extends AbstractMap ...@@ -347,7 +476,7 @@ public class TreeMap extends AbstractMap
public Iterator iterator() public Iterator iterator()
{ {
return new TreeIterator(TreeIterator.KEYS); return new TreeIterator(KEYS);
} }
public void clear() public void clear()
...@@ -357,7 +486,7 @@ public class TreeMap extends AbstractMap ...@@ -357,7 +486,7 @@ public class TreeMap extends AbstractMap
public boolean contains(Object o) public boolean contains(Object o)
{ {
return TreeMap.this.containsKey(o); return containsKey(o);
} }
public boolean remove(Object key) public boolean remove(Object key)
...@@ -365,15 +494,44 @@ public class TreeMap extends AbstractMap ...@@ -365,15 +494,44 @@ public class TreeMap extends AbstractMap
Node n = getNode(key); Node n = getNode(key);
if (n == nil) if (n == nil)
return false; return false;
TreeMap.this.removeNode(n); removeNode(n);
return true; 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) public Object put(Object key, Object value)
{ {
modCount++;
Node current = root; Node current = root;
Node parent = nil; Node parent = nil;
int comparison = 0; int comparison = 0;
...@@ -387,341 +545,414 @@ public class TreeMap extends AbstractMap ...@@ -387,341 +545,414 @@ public class TreeMap extends AbstractMap
current = current.right; current = current.right;
else if (comparison < 0) else if (comparison < 0)
current = current.left; current = current.left;
else else // Key already in tree.
{ return current.setValue(value);
// Key already in tree.
Object r = current.value;
current.value = value;
return r;
}
} }
// Set up new node. // Set up new node.
Node n = new Node(key, value); Node n = new Node(key, value, RED);
n.color = RED;
n.parent = parent; n.parent = parent;
n.left = nil;
n.right = nil;
// Insert node in tree. // Insert node in tree.
modCount++;
size++; size++;
if (parent == nil) if (parent == nil)
{ {
// Special case: inserting into an empty tree. // Special case inserting into an empty tree.
root = n; root = n;
n.color = BLACK;
return null; return null;
} }
else if (comparison > 0) if (comparison > 0)
parent.right = n; parent.right = n;
else else
parent.left = n; parent.left = n;
// Rebalance after insert. // Rebalance after insert.
insertFixup(n); insertFixup(n);
//verifyTree();
return null; return null;
} }
/** Maintain red-black balance after inserting a new node. */ /**
private void insertFixup(Node n) * 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
// Only need to rebalance when parent is a RED node, and while at least * one.
// 2 levels deep into the tree (ie: node has a grandparent). *
while (n != root && n.parent.parent != nil && n.parent.color == RED) * @param m the map to be hashed into this
{ * @throws ClassCastException if a key in m is not comparable with keys
if (n.parent == n.parent.parent.left) * in the map
{ * @throws NullPointerException if a key in m is null, and the comparator
Node uncle = n.parent.parent.right; * does not tolerate nulls
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;
}
public void putAll(Map m) public void putAll(Map m)
{ {
Iterator itr = m.entrySet().iterator(); Iterator itr = m.entrySet().iterator();
int msize = m.size(); int pos = m.size();
Map.Entry e; while (--pos >= 0)
for (int i = 0; i < msize; i++)
{ {
e = (Map.Entry) itr.next(); Map.Entry e = (Map.Entry) itr.next();
put(e.getKey(), e.getValue()); 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) public Object remove(Object key)
{ {
Node n = getNode(key); Node n = getNode(key);
if (n != nil) if (n == nil)
{ return null;
removeNode(n); removeNode(n);
return n.value; return n.value;
} }
return null;
}
// Remove node from tree. This will increment modCount and decrement size.
// Node must exist in the tree.
private void removeNode(Node node) // z
{
Node splice; // y
Node child; // x
modCount++;
size--;
// Find splice, the node at the position to actually remove from the tree. /**
if (node.left == nil || node.right == nil) * Returns the number of key-value mappings currently in this Map.
*
* @return the size
*/
public int size()
{ {
// Node to be deleted has 0 or 1 children. return size;
splice = node;
if (node.left == nil)
child = node.right;
else
child = node.left;
} }
else
/**
* 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)
{ {
// Node has 2 children. Splice is node's successor, and will be return new SubMap(fromKey, toKey);
// swapped with node since we can't remove node directly.
splice = node.right;
while (splice.left != nil)
splice = splice.left;
child = splice.right;
} }
// Unlink splice from the tree. /**
Node parent = splice.parent; * Returns a view of this Map including all entries with keys greater or
child.parent = parent; * equal to <code>fromKey</code>. The returned map is backed by the
if (parent != nil) * 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)
{ {
if (splice == parent.left) return new SubMap(fromKey, nil);
parent.left = child;
else
parent.right = child;
} }
else
root = child;
// Keep track of splice's color in case it gets changed in the swap.
int spliceColor = splice.color;
/* /**
if (splice != node) * 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()
{ {
node.key = splice.key; if (values == null)
node.value = splice.value; // 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 != node)
{ {
// Swap SPLICE for NODE. Some implementations optimize here by simply public int size()
// 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; return size;
} }
else
public Iterator iterator()
{ {
if (node.parent.left == node) return new TreeIterator(VALUES);
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;
} }
if (spliceColor == BLACK) public void clear()
deleteFixup (child); {
TreeMap.this.clear();
//verifyTree(); }
};
return values;
} }
/** Maintain red-black balance after deleting a node. */ /**
private void deleteFixup (Node node) * 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.
*
* @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 // 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) while (node != root && node.color == BLACK)
{ {
if (node == node.parent.left) if (node == parent.left)
{ {
// Rebalance left side. // Rebalance left side.
Node sibling = node.parent.right; Node sibling = parent.right;
// if (sibling == nil)
// throw new InternalError();
if (sibling.color == RED) if (sibling.color == RED)
{ {
// Case 1: Sibling is red.
// Recolor sibling and parent, and rotate parent left.
sibling.color = BLACK; sibling.color = BLACK;
node.parent.color = RED; parent.color = RED;
rotateLeft(node.parent); rotateLeft(parent);
sibling = node.parent.right; 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. // Case 2: Sibling has no red children.
// Recolor sibling, and move to parent.
sibling.color = RED; sibling.color = RED;
// Black height has been decreased, so move up the tree and node = parent;
// repeat. parent = parent.parent;
node = node.parent;
} }
else else
{ {
if (sibling.right.color == BLACK) if (sibling.right.color == BLACK)
{ {
// Case 3: Sibling has red left child. // Case 3: Sibling has red left child.
// Recolor sibling and left child, rotate sibling right.
sibling.left.color = BLACK; sibling.left.color = BLACK;
sibling.color = RED; sibling.color = RED;
rotateRight(sibling); rotateRight(sibling);
sibling = node.parent.right; sibling = parent.right;
} }
// Case 4: Sibling has red right child. Recolor sibling,
// Case 4: Sibling has red right child. // right child, and parent, and rotate parent left.
sibling.color = sibling.parent.color; sibling.color = parent.color;
sibling.parent.color = BLACK; parent.color = BLACK;
sibling.right.color = BLACK; sibling.right.color = BLACK;
rotateLeft(node.parent); rotateLeft(parent);
node = root; // Finished. node = root; // Finished.
} }
} }
else else
{ {
// Symmetric "mirror" of left-side case. // Symmetric "mirror" of left-side case.
Node sibling = node.parent.left; Node sibling = parent.left;
// if (sibling == nil)
// throw new InternalError();
if (sibling.color == RED) if (sibling.color == RED)
{ {
// Case 1: Sibling is red.
// Recolor sibling and parent, and rotate parent right.
sibling.color = BLACK; sibling.color = BLACK;
node.parent.color = RED; parent.color = RED;
rotateRight(node.parent); rotateRight(parent);
sibling = node.parent.left; sibling = parent.left;
} }
if (sibling.left.color == BLACK && sibling.right.color == BLACK) if (sibling.right.color == BLACK && sibling.left.color == BLACK)
{ {
// Case 2: Sibling has no red children.
// Recolor sibling, and move to parent.
sibling.color = RED; sibling.color = RED;
node = node.parent; node = parent;
parent = parent.parent;
} }
else else
{ {
if (sibling.left.color == BLACK) 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.right.color = BLACK;
sibling.color = RED; sibling.color = RED;
rotateLeft(sibling); rotateLeft(sibling);
sibling = node.parent.left; sibling = parent.left;
} }
// Case 4: Sibling has red left child. Recolor sibling,
sibling.color = sibling.parent.color; // left child, and parent, and rotate parent right.
sibling.parent.color = BLACK; sibling.color = parent.color;
parent.color = BLACK;
sibling.left.color = BLACK; sibling.left.color = BLACK;
rotateRight(node.parent); rotateRight(parent);
node = root; node = root; // Finished.
} }
} }
} }
node.color = BLACK; 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) if (count == 0)
return new SubMap(fromKey, toKey); return;
else
throw new IllegalArgumentException("fromKey > toKey");
}
public SortedMap headMap(Object 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
return new SubMap(nil, toKey); // 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) // Make the root node.
root = new Node(null, null, BLACK);
size = count;
Node row = root;
int rowsize;
// Fill each row that is completely full of nodes.
for (rowsize = 2; rowsize + rowsize < count; rowsize <<= 1)
{ {
return new SubMap(fromKey, nil); 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;
} }
/** Returns a "collection view" (or "bag view") of this TreeMap's values. */ // Now do the partial final row in red.
public Collection values() int overflow = count - rowsize;
Node parent = row;
int i;
for (i = 0; i < overflow; i += 2)
{ {
// We don't bother overriding many of the optional methods, as doing so Node left = new Node(null, null, RED);
// wouldn't provide any significant performance advantage. Node right = new Node(null, null, RED);
return new AbstractCollection() 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)
{ {
public int size() Node left = new Node(null, null, RED);
left.parent = parent;
parent.left = left;
parent = parent.right;
left.parent.right = nil;
}
// Unlink the remaining nodes of the previous row.
while (parent != nil)
{ {
return size; Node next = parent.right;
parent.right = nil;
parent = next;
}
} }
public Iterator iterator() /**
* Returns the first sorted node in the map, or nil if empty. Package
* visible for use by nested classes.
*
* @return the first node
*/
final Node firstNode()
{ {
return new TreeIterator(TreeIterator.VALUES); // Exploit fact that nil.left == nil.
Node node = root;
while (node.left != nil)
node = node.left;
return node;
} }
public void clear() /**
* 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)
{ {
TreeMap.this.clear(); Node current = root;
while (current != nil)
{
int comparison = compare(key, current.key);
if (comparison > 0)
current = current.right;
else if (comparison < 0)
current = current.left;
else
return current;
} }
}; return current;
} }
// Find the "highest" node which is < key. If key is nil, return last node. /**
// Note that highestLessThan is exclusive (it won't return a key which is * Find the "highest" node which is &lt; key. If key is nil, return last
// equal to "key"), while lowestGreaterThan is inclusive, in order to be * node. Package visible for use by nested classes.
// consistent with the semantics of subMap(). *
private Node highestLessThan(Object key) * @param key the upper bound, exclusive
* @return the previous node
*/
final Node highestLessThan(Object key)
{ {
if (key == nil) if (key == nil)
return lastNode(); return lastNode();
...@@ -738,20 +969,114 @@ public class TreeMap extends AbstractMap ...@@ -738,20 +969,114 @@ public class TreeMap extends AbstractMap
current = current.right; current = current.right;
else if (comparison < 0) else if (comparison < 0)
current = current.left; current = current.left;
else /* Exact match. */ else // Exact match.
return predecessor(last); return predecessor(last);
} }
if (comparison <= 0) return comparison <= 0 ? predecessor(last) : last;
return predecessor(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 else
return last; {
if (n == n.parent.right)
{
// Case 2. Uncle is BLACK and x is right child.
// Move n to parent, and rotate n left.
n = n.parent;
rotateLeft(n);
}
// Case 3. Uncle is BLACK and x is left child.
// Recolor parent, grandparent, and rotate grandparent right.
n.parent.color = BLACK;
n.parent.parent.color = RED;
rotateRight(n.parent.parent);
}
}
else
{
// Mirror image of above code.
Node uncle = n.parent.parent.left;
// Uncle may be nil, in which case it is BLACK.
if (uncle.color == RED)
{
// Case 1. Uncle is RED: Change colors of parent, uncle,
// and grandparent, and move n to grandparent.
n.parent.color = BLACK;
uncle.color = BLACK;
uncle.parent.color = RED;
n = uncle.parent;
}
else
{
if (n == n.parent.left)
{
// Case 2. Uncle is BLACK and x is left child.
// Move n to parent, and rotate n right.
n = n.parent;
rotateRight(n);
}
// Case 3. Uncle is BLACK and x is right child.
// Recolor parent, grandparent, and rotate grandparent left.
n.parent.color = BLACK;
n.parent.parent.color = RED;
rotateLeft(n.parent.parent);
}
}
}
root.color = BLACK;
}
/**
* Returns 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 >= key. If key is nil, return first node. /**
private Node lowestGreaterThan(Object key) * Find the "lowest" node which is &gt;= key. If key is nil, return either
* nil or the first node, depending on the parameter first.
* Package visible for use by nested classes.
*
* @param key the lower bound, inclusive
* @param first true to return the first element instead of nil for nil key
* @return the next node
*/
final Node lowestGreaterThan(Object key, boolean first)
{ {
if (key == nil) if (key == nil)
return firstNode(); return first ? firstNode() : nil;
Node last = nil; Node last = nil;
Node current = root; Node current = root;
...@@ -768,87 +1093,168 @@ public class TreeMap extends AbstractMap ...@@ -768,87 +1093,168 @@ public class TreeMap extends AbstractMap
else else
return current; return current;
} }
if (comparison > 0) return comparison > 0 ? successor(last) : last;
return successor(last);
else
return 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 = firstNode(); node = node.left;
out.writeInt(size); while (node.right != nil)
node = node.right;
return node;
}
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); node = parent;
out.writeObject(node.value); parent = node.parent;
node = successor(node);
} }
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 throws IOException, ClassNotFoundException
{ {
in.defaultReadObject(); fabricateTree(count);
int size = in.readInt(); Node node = firstNode();
putFromObjStream(in, size, true);
}
private int compare(Object o1, Object o2) while (--count >= 0)
{ {
if (comparator == null) node.key = s.readObject();
return ((Comparable) o1).compareTo(o2); node.value = readValues ? s.readObject() : "";
else node = successor(node);
return comparator.compare(o1, o2); }
} }
/* 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) fabricateTree(count);
Node node = firstNode();
while (--count >= 0)
{ {
node = node.right; node.key = keys.next();
while (node.left != nil) node.value = "";
node = node.left; node = successor(node);
return node; }
} }
Node parent = node.parent; /**
while (parent != nil && node == parent.right) * 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
{ {
node = parent; s.defaultReadObject();
parent = parent.parent; int size = s.readInt();
} putFromObjStream(s, size, true);
return parent;
} }
/* Return the node preceeding Node, or nil if there isn't one. */ /**
private Node predecessor(Node node) * Remove node from tree. This will increment modCount and decrement size.
* Node must exist in the tree. Package visible for use by nested classes.
*
* @param node the node to remove
*/
final void removeNode(Node node)
{ {
if (node.left != nil) Node splice;
Node child;
modCount++;
size--;
// Find splice, the node at the position to actually remove from the tree.
if (node.left == nil)
{ {
node = node.left; // Node to be deleted has 0 or 1 children.
while (node.right != nil) splice = node;
node = node.right; child = node.right;
return node; }
else if (node.right == nil)
{
// Node to be deleted has 1 child.
splice = node;
child = node.left;
}
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;
} }
Node parent = node.parent; // Unlink splice from the tree.
while (parent != nil && node == parent.left) Node parent = splice.parent;
if (child != nil)
child.parent = parent;
if (parent == nil)
{ {
node = parent; // Special case for 0 or 1 node remaining.
parent = parent.parent; root = child;
return;
} }
return parent; if (splice == parent.left)
parent.left = child;
else
parent.right = child;
if (splice.color == BLACK)
deleteFixup(child, parent);
} }
/** Rotate node n to the left. */ /**
* Rotate node n to the left.
*
* @param node the node to rotate
*/
private void rotateLeft(Node node) private void rotateLeft(Node node)
{ {
Node child = node.right; Node child = node.right;
// if (node == nil || child == nil)
// throw new InternalError();
// Establish node.right link. // Establish node.right link.
node.right = child.left; node.right = child.left;
...@@ -869,14 +1275,19 @@ public class TreeMap extends AbstractMap ...@@ -869,14 +1275,19 @@ public class TreeMap extends AbstractMap
// Link n and child. // Link n and child.
child.left = node; 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) private void rotateRight(Node node)
{ {
Node child = node.left; Node child = node.left;
// if (node == nil || child == nil)
// throw new InternalError();
// Establish node.left link. // Establish node.left link.
node.left = child.right; node.left = child.right;
...@@ -897,294 +1308,104 @@ public class TreeMap extends AbstractMap ...@@ -897,294 +1308,104 @@ public class TreeMap extends AbstractMap
// Link n and child. // Link n and child.
child.right = node; child.right = node;
if (node != nil)
node.parent = child; node.parent = child;
} }
/* Construct a tree from sorted keys in linear time. This is used to /**
implement TreeSet's SortedSet constructor. */ * Return the node following the given one, or nil if there isn't one.
void putKeysLinear(Iterator keys, int count) * Package visible for use by nested classes.
{ *
fabricateTree(count); * @param node the current node, not nil
Node node = firstNode(); * @return the next node in sorted order
*/
for (int i = 0; i < count; i++) final Node successor(Node node)
{
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.
}
/* 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)
{ {
VerifyResult vr1 = null; if (node.right != nil)
VerifyResult vr2 = null;
if (n.left == nil && n.right == nil)
{ {
// leaf node node = node.right;
VerifyResult r = new VerifyResult(); while (node.left != nil)
r.black = (n.color == BLACK ? 1 : 0); node = node.left;
r.count = 1; return node;
r.maxdepth = 1;
return r;
} }
if (n.left != nil) Node parent = node.parent;
{ // Exploit fact that nil.right == nil and node is non-nil.
if (n.left.parent != n) while (node == parent.right)
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) node = parent;
vr1.black++; parent = parent.parent;
vr1.count++;
vr1.maxdepth++;
return vr1;
} }
return parent;
} }
if (n.right != nil) /**
{ * Serializes this object to the given stream.
if (n.right.parent != n) *
verifyError(n.right, "Node's parent link does not point to " + n); * @param s the stream to write to
* @throws IOException if the underlying stream fails
if (n.color == RED && n.right.color == RED) * @serialData the <i>size</i> (int), followed by key (Object) and value
verifyError(n, "Red node has red right child"); * (Object) pairs in sorted order
*/
vr2 = verifySub (n.right); private void writeObject(ObjectOutputStream s) throws IOException
if (n.left == nil)
{ {
if (n.color == BLACK) s.defaultWriteObject();
vr2.black++;
vr2.count++;
vr2.maxdepth++;
return vr2;
}
}
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;
}
private void verifyError (Object obj, String msg) Node node = firstNode();
{ s.writeInt(size);
System.err.print ("Verify error: "); while (node != nil)
try
{
System.err.print (obj);
}
catch (Exception x)
{ {
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. * Iterate over HashMap's entries. This implementation is parameterized
* This implementation is parameterized to give a sequential view of * to give a sequential view of keys, values, or entries.
* keys, values, or entries. *
* @author Eric Blake <ebb9@email.byu.edu>
*/ */
class TreeIterator implements Iterator private final class TreeIterator implements Iterator
{ {
static final int ENTRIES = 0, /**
KEYS = 1, * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
VALUES = 2; * or {@link #ENTRIES}.
*/
// the type of this Iterator: KEYS, VALUES, or ENTRIES. private final int type;
int type; /** The number of modifications to the backing Map that we know about. */
// the number of modifications to the backing Map that we know about. private int knownMod = modCount;
int knownMod = TreeMap.this.modCount; /** The last Entry returned by a next() call. */
// The last Entry returned by a next() call. private Node last;
Node last; /** The next entry that should be returned by next(). */
// The next entry that should be returned by next(). private Node next;
Node next; /**
// The last node visible to this iterator. This is used when iterating * The last node visible to this iterator. This is used when iterating
// on a SubMap. * on a SubMap.
Node max; */
private final Node max;
/* Create Iterator with the supplied type: KEYS, VALUES, or ENTRIES */ /**
* Construct a new TreeIterator with the supplied type.
* @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
*/
TreeIterator(int type) TreeIterator(int type)
{ {
// FIXME gcj cannot handle this. Bug java/4695
// this(type, firstNode(), nil);
this.type = type; this.type = type;
this.next = firstNode(); 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) TreeIterator(int type, Node first, Node max)
{ {
this.type = type; this.type = type;
...@@ -1192,171 +1413,236 @@ public class TreeMap extends AbstractMap ...@@ -1192,171 +1413,236 @@ public class TreeMap extends AbstractMap
this.max = max; 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() public boolean hasNext()
{ {
if (knownMod != TreeMap.this.modCount) if (knownMod != modCount)
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
return (next != nil); 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() public Object next()
{ {
if (next == nil) if (knownMod != modCount)
throw new NoSuchElementException();
if (knownMod != TreeMap.this.modCount)
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
Node n = next; if (next == max)
throw new NoSuchElementException();
// Check limit in case we are iterating through a submap. last = next;
if (n != max) next = successor(last);
next = successor(n);
else
next = nil;
last = n;
if (type == VALUES) if (type == VALUES)
return n.value; return last.value;
else if (type == KEYS) else if (type == KEYS)
return n.key; return last.key;
return n; 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() public void remove()
{ {
if (knownMod != modCount)
throw new ConcurrentModificationException();
if (last == null) if (last == null)
throw new IllegalStateException(); throw new IllegalStateException();
if (knownMod != TreeMap.this.modCount)
throw new ConcurrentModificationException(); removeNode(last);
/*
Object key = null;
if (next != nil)
key = next.key;
*/
TreeMap.this.removeNode(last);
knownMod++;
/*
if (key != null)
next = getNode(key);
*/
last = null; 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; * 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 and maxKey /**
(inclusive). If minKey is nil, SubMap has no lower bound (headMap). * Create a SubMap representing the elements between minKey (inclusive)
If maxKey is nil, the SubMap has no upper bound (tailMap). */ * 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) SubMap(Object minKey, Object maxKey)
{ {
if (minKey != nil && maxKey != nil && compare(minKey, maxKey) > 0)
throw new IllegalArgumentException("fromKey > toKey");
this.minKey = minKey; this.minKey = minKey;
this.maxKey = maxKey; this.maxKey = maxKey;
} }
public void clear() /**
* 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)
{ {
Node current; return ((minKey == nil || compare(key, minKey) >= 0)
Node next = lowestGreaterThan(minKey); && (maxKey == nil || compare(key, maxKey) < 0));
Node max = highestLessThan(maxKey); }
if (compare(next.key, max.key) > 0)
// Nothing to delete.
return;
do public void clear()
{ {
current = next; Node next = lowestGreaterThan(minKey, true);
Node max = lowestGreaterThan(maxKey, false);
while (next != max)
{
Node current = next;
next = successor(current); next = successor(current);
remove(current); removeNode(current);
} }
while (current != max);
} }
/* Check if "key" is in within the range bounds for this SubMap. public Comparator comparator()
The lower ("from") SubMap range is inclusive, and the upper (to) bound
is exclusive. */
private boolean keyInRange(Object key)
{ {
return ((minKey == nil || compare(key, minKey) >= 0) return comparator;
&& (maxKey == nil || compare(key, maxKey) < 0));
} }
public boolean containsKey(Object key) public boolean containsKey(Object key)
{ {
return (keyInRange(key) && TreeMap.this.containsKey(key)); return keyInRange(key) && TreeMap.this.containsKey(key);
} }
public boolean containsValue(Object value) public boolean containsValue(Object value)
{ {
Node node = lowestGreaterThan(minKey); Node node = lowestGreaterThan(minKey, true);
Node max = highestLessThan(maxKey); Node max = lowestGreaterThan(maxKey, false);
Object currentVal; while (node != max)
if (node == nil || max == nil || compare(node.key, max.key) > 0)
// Nothing to search.
return false;
while (true)
{ {
currentVal = node.getValue(); if (equals(value, node.getValue()))
if (value == null ? currentVal == null : value.equals (currentVal))
return true; return true;
if (node == max)
return false;
node = successor(node); node = successor(node);
} }
return false;
} }
public Object get(Object key) public Set entrySet()
{ {
if (keyInRange(key)) if (entries == null)
return TreeMap.this.get(key); // Create an AbstractSet with custom implementations of those methods
return null; // that can be overriden easily and efficiently.
entries = new AbstractSet()
{
public int size()
{
return SubMap.this.size();
} }
public Object put(Object key, Object value) public Iterator iterator()
{ {
if (keyInRange(key)) Node first = lowestGreaterThan(minKey, true);
return TreeMap.this.put(key, value); Node max = lowestGreaterThan(maxKey, false);
else return new TreeIterator(ENTRIES, first, max);
throw new IllegalArgumentException("Key outside range");
} }
public Object remove(Object key) public void clear()
{ {
if (keyInRange(key)) SubMap.this.clear();
return TreeMap.this.remove(key);
else
return null;
} }
public int size() public boolean contains(Object o)
{ {
Node node = lowestGreaterThan(minKey); if (! (o instanceof Map.Entry))
Node max = highestLessThan(maxKey); 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;
}
if (node == nil || max == nil || compare(node.key, max.key) > 0) public Object firstKey()
return 0; // Empty. {
Node node = lowestGreaterThan(minKey, true);
if (node == nil || ! keyInRange(node.key))
throw new NoSuchElementException();
return node.key;
}
int count = 1; public Object get(Object key)
while (node != max)
{ {
count++; if (keyInRange(key))
node = successor(node); return TreeMap.this.get(key);
return null;
} }
return count; public SortedMap headMap(Object toKey)
{
if (! keyInRange(toKey))
throw new IllegalArgumentException("key outside range");
return new SubMap(minKey, toKey);
} }
public Set entrySet() public Set keySet()
{ {
// Create an AbstractSet with custom implementations of those methods that if (this.keys == null)
// can be overriden easily and efficiently. // Create an AbstractSet with custom implementations of those methods
return new AbstractSet() // that can be overriden easily and efficiently.
this.keys = new AbstractSet()
{ {
public int size() public int size()
{ {
...@@ -1365,38 +1651,29 @@ public class TreeMap extends AbstractMap ...@@ -1365,38 +1651,29 @@ public class TreeMap extends AbstractMap
public Iterator iterator() public Iterator iterator()
{ {
Node first = lowestGreaterThan(minKey); Node first = lowestGreaterThan(minKey, true);
Node max = highestLessThan(maxKey); Node max = lowestGreaterThan(maxKey, false);
return new TreeIterator(TreeIterator.ENTRIES, first, max); return new TreeIterator(KEYS, first, max);
} }
public void clear() public void clear()
{ {
this.clear(); SubMap.this.clear();
} }
public boolean contains(Object o) public boolean contains(Object o)
{ {
if (!(o instanceof Map.Entry)) if (! keyInRange(o))
return false; return false;
Map.Entry me = (Map.Entry) o; return getNode(o) != nil;
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) public boolean remove(Object o)
{ {
if (!(o instanceof Map.Entry)) if (! keyInRange(o))
return false; return false;
Map.Entry me = (Map.Entry) o; Node n = getNode(o);
Object key = me.getKey(); if (n != nil)
if (!keyInRange(key))
return false;
Node n = getNode(key);
if (n != nil && me.getValue().equals(n.value))
{ {
removeNode(n); removeNode(n);
return true; return true;
...@@ -1404,51 +1681,83 @@ public class TreeMap extends AbstractMap ...@@ -1404,51 +1681,83 @@ public class TreeMap extends AbstractMap
return false; return false;
} }
}; };
return this.keys;
} }
public Comparator comparator() public Object lastKey()
{ {
return comparator; Node node = highestLessThan(maxKey);
if (node == nil || ! keyInRange(node.key))
throw new NoSuchElementException();
return node.key;
} }
public Object firstKey() public Object put(Object key, Object value)
{ {
Node node = lowestGreaterThan(minKey); if (! keyInRange(key))
if (node == nil || !keyInRange(node.key)) throw new IllegalArgumentException("Key outside range");
throw new NoSuchElementException ("empty"); return TreeMap.this.put(key, value);
return node.key;
} }
public Object lastKey() public Object remove(Object key)
{ {
Node node = highestLessThan(maxKey); if (keyInRange(key))
if (node == nil || !keyInRange(node.key)) return TreeMap.this.remove(key);
throw new NoSuchElementException ("empty"); return null;
return node.key; }
public int size()
{
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) public SortedMap subMap(Object fromKey, Object toKey)
{ {
if (!keyInRange(fromKey) || !keyInRange(toKey)) if (! keyInRange(fromKey) || ! keyInRange(toKey))
throw new IllegalArgumentException("key outside range"); throw new IllegalArgumentException("key outside range");
return new SubMap(fromKey, toKey);
return TreeMap.this.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"); throw new IllegalArgumentException("key outside range");
return new SubMap(fromKey, maxKey);
}
return TreeMap.this.subMap(minKey, toKey); public Collection values()
{
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();
} }
public SortedMap tailMap(Object fromKey) public Iterator iterator()
{ {
if (!keyInRange(fromKey)) Node first = lowestGreaterThan(minKey, true);
throw new IllegalArgumentException("key outside range"); Node max = lowestGreaterThan(maxKey, false);
return new TreeIterator(VALUES, first, max);
}
return TreeMap.this.subMap(fromKey, maxKey); public void clear()
{
SubMap.this.clear();
} }
};
return this.keys;
} }
} } // class SubMap
} // class TreeMap
/* 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. Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This file is part of GNU Classpath. This file is part of GNU Classpath.
...@@ -33,30 +33,62 @@ import java.io.ObjectInputStream; ...@@ -33,30 +33,62 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
/** /**
* This class provides a TreeMap-backed implementation of the * This class provides a TreeMap-backed implementation of the SortedSet
* SortedSet interface. * 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 * Most operations are O(log n), but there is so much overhead that this
* maps to a static token, denoting that the key does, in fact, exist. * 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 public class TreeSet extends AbstractSet
implements SortedSet, Cloneable, Serializable 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" * Construct a new TreeSet whose backing TreeMap using the "natural"
* ordering of keys. * ordering of keys. Elements that are not mutually comparable will cause
* ClassCastExceptions down the road.
*
* @see Comparable
*/ */
public TreeSet() public TreeSet()
{ {
...@@ -65,9 +97,10 @@ public class TreeSet extends AbstractSet ...@@ -65,9 +97,10 @@ public class TreeSet extends AbstractSet
/** /**
* Construct a new TreeSet whose backing TreeMap uses the supplied * Construct a new TreeSet whose backing TreeMap uses the supplied
* Comparator. * 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) public TreeSet(Comparator comparator)
{ {
...@@ -77,10 +110,14 @@ public class TreeSet extends AbstractSet ...@@ -77,10 +110,14 @@ public class TreeSet extends AbstractSet
/** /**
* Construct a new TreeSet whose backing TreeMap uses the "natural" * Construct a new TreeSet whose backing TreeMap uses the "natural"
* orering of the keys and which contains all of the elements in the * 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 * @param collection the new Set will be initialized with all
* of the elements in this Collection * 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) public TreeSet(Collection collection)
{ {
...@@ -93,54 +130,57 @@ public class TreeSet extends AbstractSet ...@@ -93,54 +130,57 @@ public class TreeSet extends AbstractSet
* SortedSet and containing all of the elements in the supplied SortedSet. * SortedSet and containing all of the elements in the supplied SortedSet.
* This constructor runs in linear time. * This constructor runs in linear time.
* *
* @param sortedSet the new TreeSet will use this SortedSet's * @param sortedSet the new TreeSet will use this SortedSet's comparator
* comparator and will initialize itself * and will initialize itself with all its elements
* with all of the elements in this SortedSet * @throws NullPointerException if sortedSet is null
*/ */
public TreeSet(SortedSet sortedSet) public TreeSet(SortedSet sortedSet)
{ {
TreeMap map = new TreeMap(sortedSet.comparator()); map = new TreeMap(sortedSet.comparator());
int i = 0;
Iterator itr = sortedSet.iterator(); Iterator itr = sortedSet.iterator();
map.putKeysLinear(itr, sortedSet.size()); ((TreeMap) map).putKeysLinear(itr, sortedSet.size());
this.map = map;
} }
/* This private constructor is used to implement the subSet() calls around /**
a backing TreeMap.SubMap. */ * This private constructor is used to implement the subSet() calls around
TreeSet(SortedMap backingMap) * a backing TreeMap.SubMap.
*
* @param backingMap the submap
*/
private TreeSet(SortedMap backingMap)
{ {
map = backingMap; map = backingMap;
} }
/** /**
* Adds the spplied Object to the Set if it is not already in the Set; * 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) 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. * Adds all of the elements in the supplied Collection to this TreeSet.
* *
* @param c All of the elements in this Collection * @param c The collection to add
* will be added to the Set.
*
* @return true if the Set is altered, false otherwise * @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) public boolean addAll(Collection c)
{ {
boolean result = false; boolean result = false;
int size = c.size(); int pos = c.size();
Iterator itr = c.iterator(); Iterator itr = c.iterator();
while (--pos >= 0)
for (int i = 0; i < size; i++) result |= (map.put(itr.next(), "") == null);
result |= (map.put(itr.next(), Boolean.TRUE) == null);
return result; return result;
} }
...@@ -152,137 +192,214 @@ public class TreeSet extends AbstractSet ...@@ -152,137 +192,214 @@ public class TreeSet extends AbstractSet
map.clear(); 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() public Object clone()
{ {
TreeSet copy = null; TreeSet copy = null;
try try
{ {
copy = (TreeSet) super.clone(); 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) catch (CloneNotSupportedException x)
{ {
// Impossible result.
} }
copy.map = (SortedMap) ((TreeMap) map).clone();
return copy; 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() public Comparator comparator()
{ {
return map.comparator(); return map.comparator();
} }
/** /**
* Returns true if this Set contains the supplied Object, * Returns true if this Set contains the supplied Object, false otherwise.
* false otherwise
* *
* @param oObject the Object whose existence in the Set is * @param obj the Object to check for
* being tested * @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) public boolean contains(Object obj)
{ {
return map.containsKey(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 * Returns true if this Set has size 0, false otherwise.
* returned; otherwise, false is returned.
* *
* @param obj the Object we are attempting to remove * @return true if the set is empty
* from this Set
*/ */
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() public Object last()
{ {
return map.lastKey(); return map.lastKey();
} }
/** /**
* Returns a view of this Set including all elements in the interval * If the supplied Object is in this Set, it is removed, and true is
* [oFromElement, oToElement). * returned; otherwise, false is returned.
* *
* @param from the resultant view will contain all * @param obj the Object to remove from this Set
* elements greater than or equal to this element * @return true if the set was modified
* @param to the resultant view will contain all * @throws ClassCastException if obj cannot be compared to set elements
* elements less than this element
*/ */
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 * @return the set size
* elements less than this element
*/ */
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 * Returns a view of this Set including all elements greater or equal to
* equal to oFromElement. * <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 * @param from the (inclusive) low cutoff point
* elements greater than or equal to this element * @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(); Iterator itr = map.keySet().iterator();
int size = map.size(); int pos = map.size();
s.writeObject(map.comparator());
out.writeObject(map.comparator()); s.writeInt(pos);
out.writeInt(size); while (--pos >= 0)
s.writeObject(itr.next());
for (int i = 0; i < size; i++)
out.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 throws IOException, ClassNotFoundException
{ {
Comparator comparator = (Comparator) in.readObject(); s.defaultReadObject();
int size = in.readInt(); Comparator comparator = (Comparator) s.readObject();
TreeMap map = new TreeMap(comparator); int size = s.readInt();
map.putFromObjStream(in, size, false); map = new TreeMap(comparator);
this.map = map; ((TreeMap) map).putFromObjStream(s, size, false);
} }
} }
/* Vector.java -- Class that provides growable arrays. /* 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. This file is part of GNU Classpath.
...@@ -30,51 +30,73 @@ import java.lang.reflect.Array; ...@@ -30,51 +30,73 @@ import java.lang.reflect.Array;
import java.io.Serializable; 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 * 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 * 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 * 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 * 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
* @specnote The JCL claims that various methods in this class throw * code structurally modifies the vector, any operation on the iterator will
* IndexOutOfBoundsException, which would be consistent with other collections * then throw a {@link ConcurrentModificationException}. The Vector class is
* classes. ArrayIndexOutOfBoundsException is actually thrown, per the online * fully synchronized, but the iterators are not. So, when iterating over a
* docs, even for List method implementations. * 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.
* *
* @author Scott G. Miller * @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 public class Vector extends AbstractList
implements List, Cloneable, Serializable implements List, RandomAccess, Cloneable, Serializable
{ {
/** /**
* The amount the Vector's internal array should be increased in size when * Compatible with JDK 1.0+.
* a new element is added that exceeds the current size of the array, */
* or when {@link #ensureCapacity} is called. private static final long serialVersionUID = -2767605614048989439L;
* @serial
/**
* 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 int capacityIncrement = 0; protected Object[] elementData;
/** /**
* The number of elements currently in the vector, also returned by * The number of elements currently in the vector, also returned by
* {@link #size}. * {@link #size}.
* @serial * @serial the size
*/ */
protected int elementCount = 0; protected int elementCount;
/** /**
* The internal array used to hold members of a Vector * The amount the Vector's internal array should be increased in size when
* @serial * 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; protected int capacityIncrement;
private static final long serialVersionUID = -2767605614048989439L;
/** /**
* Constructs an empty vector with an initial size of 10, and * Constructs an empty vector with an initial size of 10, and
...@@ -82,36 +104,31 @@ public class Vector extends AbstractList ...@@ -82,36 +104,31 @@ public class Vector extends AbstractList
*/ */
public Vector() public Vector()
{ {
this(10); this(10, 0);
} }
/** /**
* Constructs a vector containing the contents of Collection, in the * 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 * @param c collection of elements to add to the new vector
* vector * @throws NullPointerException if c is null
* @since 1.2
*/ */
public Vector(Collection c) public Vector(Collection c)
{ {
int csize = c.size(); elementCount = c.size();
elementData = new Object[csize]; elementData = c.toArray(new Object[elementCount]);
elementCount = csize;
Iterator itr = c.iterator();
for (int i = 0; i < csize; i++)
{
elementData[i] = itr.next();
}
} }
/** /**
* Constructs a Vector with the initial capacity and capacity * Constructs a Vector with the initial capacity and capacity
* increment specified * increment specified.
* *
* @param initialCapacity The initial size of the Vector's internal * @param initialCapacity the initial size of the Vector's internal array
* array * @param capacityIncrement the amount the internal array should be
* @param capacityIncrement The amount the internal array should be * increased by when necessary, 0 to double the size
* increased if necessary * @throws IllegalArgumentException if initialCapacity &lt; 0
*/ */
public Vector(int initialCapacity, int capacityIncrement) public Vector(int initialCapacity, int capacityIncrement)
{ {
...@@ -122,37 +139,37 @@ public class Vector extends AbstractList ...@@ -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) public Vector(int initialCapacity)
{ {
if (initialCapacity < 0) this(initialCapacity, 0);
throw new IllegalArgumentException();
elementData = new Object[initialCapacity];
} }
/** /**
* Copies the contents of a provided array into the Vector. If the * Copies the contents of a provided array into the Vector. If the
* array is too large to fit in the Vector, an ArrayIndexOutOfBoundsException * array is too large to fit in the Vector, an IndexOutOfBoundsException
* is thrown. Old elements in the Vector are overwritten by the new * is thrown without modifying the array. Old elements in the Vector are
* elements * overwritten by the new elements.
* *
* @param anArray An array from which elements will be copied into the Vector * @param a target array for the copy
* * @throws IndexOutOfBoundsException the array is not large enough
* @throws ArrayIndexOutOfBoundsException the array being copied * @throws NullPointerException the array is null
* is larger than the Vectors internal data array * @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 * 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 * 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() public synchronized void trimToSize()
{ {
...@@ -166,15 +183,14 @@ public class Vector extends AbstractList ...@@ -166,15 +183,14 @@ public class Vector extends AbstractList
} }
/** /**
* Ensures that <b>minCapacity</b> elements can fit within this Vector. * Ensures that <code>minCapacity</code> elements can fit within this Vector.
* If it cannot hold this many elements, the internal data array is expanded * If <code>elementData</code> is too small, it is expanded as follows:
* in the following manner. If the current size plus the capacityIncrement * If the <code>elementCount + capacityIncrement</code> is adequate, that
* is sufficient, the internal array is expanded by capacityIncrement. * is the new size. If <code>capacityIncrement</code> is non-zero, the
* If capacityIncrement is non-positive, the size is doubled. If * candidate size is double the current. If that is not enough, the new
* neither is sufficient, the internal array is expanded to size minCapacity * size is <code>minCapacity</code>.
* *
* @param minCapacity The minimum capacity the internal array should be * @param minCapacity the desired minimum capacity, negative values ignored
* able to handle after executing this method
*/ */
public synchronized void ensureCapacity(int minCapacity) public synchronized void ensureCapacity(int minCapacity)
{ {
...@@ -189,45 +205,48 @@ public class Vector extends AbstractList ...@@ -189,45 +205,48 @@ public class Vector extends AbstractList
Object[] newArray = new Object[Math.max(newCapacity, minCapacity)]; 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; elementData = newArray;
} }
/** /**
* Explicitly sets the size of the internal data array, copying the * Explicitly sets the size of the vector (but not necessarily the size of
* old values to the new internal array. If the new array is smaller * the internal data array). If the new size is smaller than the old one,
* than the old one, old values that don't fit are lost. If the new size * old values that don't fit are lost. If the new size is larger than the
* is larger than the old one, the vector is padded with null entries. * old one, the vector is padded with null entries.
* *
* @param newSize The new size of the internal array * @param newSize The new size of the internal array
* @throws ArrayIndexOutOfBoundsException if the new size is negative
*/ */
public synchronized void setSize(int newSize) 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++; modCount++;
Object[] newArray = new Object[newSize]; ensureCapacity(newSize);
System.arraycopy(elementData, 0, newArray, 0, if (newSize < elementCount)
Math.min(newSize, elementCount)); Arrays.fill(elementData, newSize, elementCount, null);
elementCount = newSize; elementCount = newSize;
elementData = newArray;
} }
/** /**
* Returns the size of the internal data array (not the amount of elements * 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; 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; return elementCount;
} }
...@@ -235,85 +254,89 @@ public class Vector extends AbstractList ...@@ -235,85 +254,89 @@ public class Vector extends AbstractList
/** /**
* Returns true if this Vector is empty, false otherwise * 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; return elementCount == 0;
} }
/** /**
* Searches the vector starting at <b>index</b> for object <b>elem</b> * Returns an Enumeration of the elements of this Vector. The enumeration
* and returns the index of the first occurrence of this Object. If * visits the elements in increasing index order, but is NOT thread-safe.
* the object is not found, -1 is returned
* *
* @param e The Object to search for * @return an Enumeration
* @param index Start searching at this index * @see #iterator()
* @returns The index of the first occurrence of <b>elem</b>, or -1
* if it is not found
*/ */
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()
{ {
if (e == null ? elementData[i] == null : e.equals(elementData[i])) private int i = 0;
return i;
public boolean hasMoreElements()
{
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 * Returns true when <code>elem</code> is contained in this Vector.
* <b>elem</b> is not found.
* *
* @param elem The object to search for * @param elem the element to check
* @returns The index of the first occurrence of <b>elem</b> or -1 if * @return true if the object is contained in this Vector, false otherwise
* not found
*/ */
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 * @param elem the object to search for
* @returns true if the object is contained in this Vector, false otherwise * @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 * Searches the vector starting at <code>index</code> for object
* backwards from <b>index</b>. If the object does not occur in this Vector, * <code>elem</code> and returns the index of the first occurrence of this
* -1 is returned. * 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 e the Object to search for
* @param index The index to start searching in reverse from * @param index start searching at this index
* @returns The index of the Object if found, -1 otherwise * @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])) for (int i = index; i < elementCount; i++)
if (equals(e, elementData[i]))
return i; return i;
}
return -1; return -1;
} }
/** /**
* Returns the last index of <b>elem</b> within this Vector, or -1 * Returns the last index of <code>elem</code> within this Vector, or -1
* if the object is not within the Vector * if the object is not within the Vector.
* *
* @param elem The object to search for * @param elem the object to search for
* @returns the last index of the object, or -1 if not found * @return the last index of the object, or -1 if not found
*/ */
public int lastIndexOf(Object elem) public int lastIndexOf(Object elem)
{ {
...@@ -321,29 +344,42 @@ public class Vector extends AbstractList ...@@ -321,29 +344,42 @@ public class Vector extends AbstractList
} }
/** /**
* Returns the Object stored at <b>index</b>. If index is out of range * Returns the index of the first occurrence of <code>elem</code>, when
* an ArrayIndexOutOfBoundsException is thrown. * 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 * @param index the index of the Object to retrieve
* @returns The object at <b>index</b> * @return the object at <code>index</code>
* @throws ArrayIndexOutOfBoundsException <b>index</b> is * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
* larger than the Vector * @see #get(int)
*/ */
public synchronized Object elementAt(int index) public synchronized Object elementAt(int index)
{ {
//Within the bounds of this Vector does not necessarily mean within checkBoundExclusive(index);
//the bounds of the internal array
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
return elementData[index]; return elementData[index];
} }
/** /**
* Returns the first element in the Vector. If there is no first Object * Returns the first element (index 0) in the Vector.
* (The vector is empty), a NoSuchElementException is thrown.
* *
* @returns The first Object in the Vector * @return the first Object in the Vector
* @throws NoSuchElementException the Vector is empty * @throws NoSuchElementException the Vector is empty
*/ */
public synchronized Object firstElement() public synchronized Object firstElement()
...@@ -351,14 +387,13 @@ public class Vector extends AbstractList ...@@ -351,14 +387,13 @@ public class Vector extends AbstractList
if (elementCount == 0) if (elementCount == 0)
throw new NoSuchElementException(); throw new NoSuchElementException();
return elementAt(0); return elementData[0];
} }
/** /**
* Returns the last element in the Vector. If the Vector has no last element * Returns the last element in the Vector.
* (The vector is empty), a NoSuchElementException is thrown.
* *
* @returns The last Object in the Vector * @return the last Object in the Vector
* @throws NoSuchElementException the Vector is empty * @throws NoSuchElementException the Vector is empty
*/ */
public synchronized Object lastElement() public synchronized Object lastElement()
...@@ -366,95 +401,61 @@ public class Vector extends AbstractList ...@@ -366,95 +401,61 @@ public class Vector extends AbstractList
if (elementCount == 0) if (elementCount == 0)
throw new NoSuchElementException(); throw new NoSuchElementException();
return elementAt(elementCount - 1); return elementData[elementCount - 1];
} }
/** /**
* Places <b>obj</b> at <b>index</b> within the Vector. If <b>index</b> * Changes the element at <code>index</code> to be <code>obj</code>
* refers to an index outside the Vector, an ArrayIndexOutOfBoundsException
* is thrown.
* *
* @param obj The object to store * @param obj the object to store
* @param index The position in the Vector to store the object * @param index the position in the Vector to store the object
* @throws ArrayIndexOutOfBoundsException the index is out of range * @throws ArrayIndexOutOfBoundsException the index is out of range
* @see #set(int, Object)
*/ */
public synchronized void setElementAt(Object obj, int index) public void setElementAt(Object obj, int index)
{ {
if (index >= elementCount) set(index, obj);
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
elementData[index] = obj;
} }
/** /**
* Puts <b>element</b> into the Vector at position <b>index</b> and returns * Removes the element at <code>index</code>, and shifts all elements at
* the Object that previously occupied that position.
*
* @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
* @throws ArrayIndexOutOfBoundsException the index is out of range
*
*/
public synchronized Object set(int index, Object element)
{
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
Object temp = elementData[index];
elementData[index] = element;
return temp;
}
/**
* Removes the element at <b>index</b>, and shifts all elements at
* positions greater than index to their index - 1. * 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) remove(index);
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;
} }
/** /**
* 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. * at or greater than index are shifted up one position.
* *
* @param obj The object to insert * @param obj the object to insert
* @param index The index at which the object is inserted * @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) checkBoundInclusive(index);
throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
if (elementCount == elementData.length) if (elementCount == elementData.length)
ensureCapacity(elementCount + 1); ensureCapacity(elementCount + 1);
++modCount; modCount++;
++elementCount;
System.arraycopy(elementData, index, elementData, index + 1, System.arraycopy(elementData, index, elementData, index + 1,
elementCount - 1 - index); elementCount - index);
elementCount++;
elementData[index] = obj; elementData[index] = obj;
} }
/** /**
* Adds an element to the Vector at the end of the Vector. If the vector * Adds an element to the Vector at the end of the Vector. The vector
* cannot hold the element with its present capacity, its size is increased * is increased by ensureCapacity(size() + 1) if needed.
* based on the same rules followed if ensureCapacity was called with the
* argument currentSize+1.
* *
* @param obj The object to add to the Vector * @param obj the object to add to the Vector
*/ */
public synchronized void addElement(Object obj) public synchronized void addElement(Object obj)
{ {
...@@ -465,19 +466,20 @@ public class Vector extends AbstractList ...@@ -465,19 +466,20 @@ public class Vector extends AbstractList
} }
/** /**
* Removes the first occurrence of the given object from the Vector. * Removes the first (the lowestindex) occurance of the given object from
* If such a remove was performed (the object was found), true is returned. * the Vector. If such a remove was performed (the object was found), true
* If there was no such object, false is returned. * is returned. If there was no such object, false is returned.
* *
* @param obj The object to remove from the Vector * @param obj the object to remove from the Vector
* @returns true if the Object was in the Vector, false otherwise * @return true if the Object was in the Vector, false otherwise
* @see #remove(Object)
*/ */
public synchronized boolean removeElement(Object obj) public synchronized boolean removeElement(Object obj)
{ {
int idx = indexOf(obj); int idx = indexOf(obj, 0);
if (idx != -1) if (idx >= 0)
{ {
removeElementAt(idx); remove(idx);
return true; return true;
} }
return false; return false;
...@@ -486,22 +488,24 @@ public class Vector extends AbstractList ...@@ -486,22 +488,24 @@ public class Vector extends AbstractList
/** /**
* Removes all elements from the Vector. Note that this does not * Removes all elements from the Vector. Note that this does not
* resize the internal data array. * resize the internal data array.
*
* @see #clear()
*/ */
public synchronized void removeAllElements() public synchronized void removeAllElements()
{ {
modCount++;
if (elementCount == 0) if (elementCount == 0)
return; return;
for (int i = elementCount - 1; i >= 0; --i) modCount++;
{ Arrays.fill(elementData, 0, elementCount, null);
elementData[i] = null;
}
elementCount = 0; 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() public synchronized Object clone()
{ {
...@@ -513,6 +517,7 @@ public class Vector extends AbstractList ...@@ -513,6 +517,7 @@ public class Vector extends AbstractList
} }
catch (CloneNotSupportedException ex) catch (CloneNotSupportedException ex)
{ {
// Impossible to get here.
throw new InternalError(ex.toString()); throw new InternalError(ex.toString());
} }
} }
...@@ -524,8 +529,8 @@ public class Vector extends AbstractList ...@@ -524,8 +529,8 @@ public class Vector extends AbstractList
* within the Vector. This is similar to creating a new Object[] with the * within the Vector. This is similar to creating a new Object[] with the
* size of this Vector, then calling Vector.copyInto(yourArray). * 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() public synchronized Object[] toArray()
{ {
...@@ -539,72 +544,93 @@ public class Vector extends AbstractList ...@@ -539,72 +544,93 @@ public class Vector extends AbstractList
* If the provided array is large enough, the contents are copied * 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 * 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 * of the null element, if you know the vector does not itself contain
* hold the elements, an ArrayStoreException is thrown. * null entries. If the array is not large enough, reflection is used
* * to create a bigger one of the same runtime type.
* 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.
* *
* * @param a an array to copy the Vector into if large enough
* @param array An array to copy the Vector into if large enough * @return an array with the contents of this Vector in order
* @returns An array with the contents of this Vector in order
* @throws ArrayStoreException the runtime type of the provided array * @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) if (a.length < elementCount)
array = (Object[]) Array.newInstance(array.getClass().getComponentType(), a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
elementCount); elementCount);
else if (array.length > elementCount) else if (a.length > elementCount)
array[elementCount] = null; a[elementCount] = null;
System.arraycopy(elementData, 0, array, 0, elementCount); System.arraycopy(elementData, 0, a, 0, elementCount);
return array; 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 * @param index the position from which an element will be retrieved
* @throws ArrayIndexOutOfBoundsException the index is not within the * @return the element at that position
* range of the Vector * @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); return elementAt(index);
} }
/** /**
* Removes the given Object from the Vector. If it exists, true * Puts <code>element</code> into the Vector at position <code>index</code>
* is returned, if not, false is returned. * and returns the Object that previously occupied that position.
* *
* @param o The object to remove from the Vector * @param index the index within the Vector to place the Object
* @returns true if the Object existed in the Vector, false otherwise * @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. * 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); addElement(o);
return true; 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 * Adds an object at the specified index. Elements at or above
* index are shifted up one position. * index are shifted up one position.
* *
* @param index The index at which to add the element * @param index the index at which to add the element
* @param element The element to add to the Vector * @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) public void add(int index, Object element)
{ {
...@@ -614,153 +640,253 @@ public class Vector extends AbstractList ...@@ -614,153 +640,253 @@ public class Vector extends AbstractList
/** /**
* Removes the element at the specified index, and returns it. * Removes the element at the specified index, and returns it.
* *
* @param index The position from which to remove the element * @param index the position from which to remove the element
* @returns The object removed * @return the object removed
* @throws ArrayIndexOutOfBoundsException the index was out of the range * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
* of the Vector * @since 1.2
*/ */
public synchronized Object remove(int index) public synchronized Object remove(int index)
{ {
if (index >= elementCount) checkBoundExclusive(index);
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
Object temp = elementData[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; 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() public void clear()
{ {
removeAllElements(); 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) public synchronized boolean containsAll(Collection c)
{ {
Iterator itr = c.iterator(); // Here just for the sychronization.
int size = c.size(); return super.containsAll(c);
for (int pos = 0; pos < size; pos++)
{
if (!contains(itr.next()))
return false;
}
return true;
} }
/**
* 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) public synchronized boolean addAll(Collection c)
{ {
return addAll(elementCount, 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) 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) 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) public synchronized boolean addAll(int index, Collection c)
{ {
if (index < 0 || index > elementCount) checkBoundInclusive(index);
throw new ArrayIndexOutOfBoundsException(index);
modCount++;
Iterator itr = c.iterator(); Iterator itr = c.iterator();
int csize = c.size(); int csize = c.size();
modCount++;
ensureCapacity(elementCount + csize); ensureCapacity(elementCount + csize);
int end = index + csize; int end = index + csize;
if (elementCount > 0 && index != elementCount) if (elementCount > 0 && index != elementCount)
System.arraycopy(elementData, index, elementData, end, csize); System.arraycopy(elementData, index, elementData, end, csize);
elementCount += csize; elementCount += csize;
for (; index < end; index++) for ( ; index < end; index++)
{
elementData[index] = itr.next(); elementData[index] = itr.next();
}
return (csize > 0); 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() public synchronized int hashCode()
{ {
// Here just for the sychronization.
return super.hashCode(); return super.hashCode();
} }
/** /**
* Returns a string representation of this Vector in the form * Returns a string representation of this Vector in the form
* [element0, element1, ... elementN] * "[element0, element1, ... elementN]".
* *
* @returns the String representation of this Vector * @return the String representation of this Vector
*/ */
public synchronized String toString() public synchronized String toString()
{ {
String r = "["; // Here just for the sychronization.
for (int i = 0; i < elementCount; i++) return super.toString();
{
r += elementData[i];
if (i < elementCount - 1)
r += ", ";
}
r += "]";
return r;
} }
/** /**
* Returns an Enumeration of the elements of this List. * Obtain a List view of a subsection of this list, from fromIndex
* The Enumeration returned is compatible behavior-wise with * (inclusive) to toIndex (exclusive). If the two indices are equal, the
* the 1.1 elements() method, in that it does not check for * sublist is empty. The returned list is modifiable, and changes in one
* concurrent modification. * 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() public synchronized List subList(int fromIndex, int toIndex)
{
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)
{ {
List sub = super.subList(fromIndex, 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 * Removes a range of elements from this list.
* will not be synchronized w.r.t. the Vector object. *
* @param fromIndex the index to start deleting from (inclusive)
* @param toIndex the index to delete up to (exclusive)
*/ */
protected synchronized void removeRange(int fromIndex, int toIndex) // 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) if (fromIndex != toIndex)
{ {
modCount++;
System.arraycopy(elementData, toIndex, elementData, fromIndex, System.arraycopy(elementData, toIndex, elementData, fromIndex,
elementCount - toIndex); elementCount - toIndex);
// Clear unused elements so objects can be collected.
int save = elementCount; int save = elementCount;
elementCount -= (toIndex - fromIndex); elementCount -= toIndex - fromIndex;
for (int i = elementCount; i < save; ++i) Arrays.fill(elementData, elementCount, save, null);
elementData[i] = 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);
} }
} }
/* java.util.WeakHashMap /* java.util.WeakHashMap -- a hashtable that keeps only weak references
Copyright (C) 1999, 2000 Free Software Foundation, Inc. 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. This file is part of GNU Classpath.
...@@ -26,6 +27,7 @@ executable file might be covered by the GNU General Public License. */ ...@@ -26,6 +27,7 @@ executable file might be covered by the GNU General Public License. */
package java.util; package java.util;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
...@@ -51,22 +53,26 @@ import java.lang.ref.ReferenceQueue; ...@@ -51,22 +53,26 @@ import java.lang.ref.ReferenceQueue;
* entry, that was in the set before, suddenly disappears. <br> * entry, that was in the set before, suddenly disappears. <br>
* *
* A weak hash map is not meant for caches; use a normal map, with * A weak hash map is not meant for caches; use a normal map, with
* soft references as values instead. <br> * soft references as values instead, or try {@link LinkedHashMap}. <br>
* *
* The weak hash map supports null values and null keys. Null keys * The weak hash map supports null values and null keys. The null key
* are never deleted from the map (except explictly of course). * 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 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 * value object maintains a strong reference to the key (either direct
* or indirect) the key will never be removed from this map. According * 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. * 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 HashMap
* @see WeakReference */ * @see WeakReference
* @see LinkedHashMap
* @since 1.2
* @status updated to 1.4
*/
public class WeakHashMap extends AbstractMap implements Map public class WeakHashMap extends AbstractMap implements Map
{ {
/** /**
...@@ -77,7 +83,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -77,7 +83,7 @@ public class WeakHashMap extends AbstractMap implements Map
private static final int DEFAULT_CAPACITY = 11; 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; private static final float DEFAULT_LOAD_FACTOR = 0.75F;
...@@ -85,18 +91,41 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -85,18 +91,41 @@ public class WeakHashMap extends AbstractMap implements Map
* This is used instead of the key value <i>null</i>. It is needed * 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 * The reference queue where our buckets (which are WeakReferences) are
* registered to. * registered to.
*/ */
private ReferenceQueue queue; private final ReferenceQueue queue;
/** /**
* The number of entries in this hash map. * 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 * The load factor of this WeakHashMap. This is the maximum ratio of
...@@ -119,17 +148,20 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -119,17 +148,20 @@ public class WeakHashMap extends AbstractMap implements Map
* by the garbage collection. Instead the iterators must make * by the garbage collection. Instead the iterators must make
* sure to have strong references to the entries they rely on. * 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 * The entry set. There is only one instance per hashmap, namely
* theEntrySet. Note that the entry set may silently shrink, just * theEntrySet. Note that the entry set may silently shrink, just
* like the WeakHashMap. * 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() public int size()
{ {
...@@ -138,6 +170,8 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -138,6 +170,8 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Returns an iterator for all entries. * Returns an iterator for all entries.
*
* @return an Entry iterator
*/ */
public Iterator iterator() public Iterator iterator()
{ {
...@@ -155,7 +189,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -155,7 +189,7 @@ public class WeakHashMap extends AbstractMap implements Map
* being removed under us, since the entry strongly refers * being removed under us, since the entry strongly refers
* to the key. * to the key.
*/ */
WeakBucket.Entry lastEntry; WeakBucket.WeakEntry lastEntry;
/** /**
* The entry that will be returned by the next * The entry that will be returned by the next
...@@ -166,11 +200,11 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -166,11 +200,11 @@ public class WeakHashMap extends AbstractMap implements Map
* being removed under us, since the entry strongly refers * being removed under us, since the entry strongly refers
* to the key. * to the key.
*/ */
WeakBucket.Entry nextEntry = findNext(null); WeakBucket.WeakEntry nextEntry = findNext(null);
/** /**
* The known number of modification to the list, if it differs * The known number of modification to the list, if it differs
* from the real number, we through an exception. * from the real number, we throw an exception.
*/ */
int knownMod = modCount; int knownMod = modCount;
...@@ -178,12 +212,13 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -178,12 +212,13 @@ public class WeakHashMap extends AbstractMap implements Map
* Check the known number of modification to the number of * Check the known number of modification to the number of
* modifications of the table. If it differs from the real * modifications of the table. If it differs from the real
* number, we throw an exception. * number, we throw an exception.
* @exception ConcurrentModificationException if the number * @throws ConcurrentModificationException if the number
* of modifications doesn't match. * of modifications doesn't match.
*/ */
private void checkMod() private void checkMod()
{ {
/* This method will get inlined */ // This method will get inlined.
cleanQueue();
if (knownMod != modCount) if (knownMod != modCount)
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
} }
...@@ -191,11 +226,11 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -191,11 +226,11 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Get a strong reference to the next entry after * Get a strong reference to the next entry after
* lastBucket. * lastBucket.
* @param lastBucket the previous bucket, or null if we should * @param lastEntry the previous bucket, or null if we should
* get the first entry. * get the first entry.
* @return the next entry. * @return the next entry.
*/ */
private WeakBucket.Entry findNext(WeakBucket.Entry lastEntry) private WeakBucket.WeakEntry findNext(WeakBucket.WeakEntry lastEntry)
{ {
int slot; int slot;
WeakBucket nextBucket; WeakBucket nextBucket;
...@@ -214,48 +249,45 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -214,48 +249,45 @@ public class WeakHashMap extends AbstractMap implements Map
{ {
while (nextBucket != null) while (nextBucket != null)
{ {
WeakBucket.Entry entry = nextBucket.getEntry(); WeakBucket.WeakEntry entry = nextBucket.getEntry();
if (entry != null) if (entry != null)
/* This is the next entry */ // This is the next entry.
return entry; return entry;
/* entry was cleared, try next */ // Entry was cleared, try next.
nextBucket = nextBucket.next; nextBucket = nextBucket.next;
} }
slot++; slot++;
if (slot == buckets.length) if (slot == buckets.length)
/* No more buckets, we are through */ // No more buckets, we are through.
return null; return null;
nextBucket = buckets[slot]; nextBucket = buckets[slot];
} }
} }
/** /**
* Checks if there are more entries. * Checks if there are more entries.
* @return true, iff there are more elements. * @return true, iff there are more elements.
* @exception IllegalModificationException if the hash map was * @throws ConcurrentModificationException if the hash map was
* modified. * modified.
*/ */
public boolean hasNext() public boolean hasNext()
{ {
cleanQueue();
checkMod(); checkMod();
return (nextEntry != null); return nextEntry != null;
} }
/** /**
* Returns the next entry. * Returns the next entry.
* @return the next entry. * @return the next entry.
* @exception IllegalModificationException if the hash map was * @throws ConcurrentModificationException if the hash map was
* modified. * modified.
* @exception NoSuchElementException if there is no entry. * @throws NoSuchElementException if there is no entry.
*/ */
public Object next() public Object next()
{ {
cleanQueue();
checkMod(); checkMod();
if (nextEntry == null) if (nextEntry == null)
throw new NoSuchElementException(); throw new NoSuchElementException();
...@@ -267,21 +299,20 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -267,21 +299,20 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Removes the last returned entry from this set. This will * Removes the last returned entry from this set. This will
* also remove the bucket of the underlying weak hash map. * also remove the bucket of the underlying weak hash map.
* @exception IllegalModificationException if the hash map was * @throws ConcurrentModificationException if the hash map was
* modified. * modified.
* @exception IllegalStateException if <code>next()</code> was * @throws IllegalStateException if <code>next()</code> was
* never called or the element was already removed. * never called or the element was already removed.
*/ */
public void remove() public void remove()
{ {
cleanQueue();
checkMod(); checkMod();
if (lastEntry == null) if (lastEntry == null)
throw new IllegalStateException(); throw new IllegalStateException();
modCount++;
internalRemove(lastEntry.getBucket()); internalRemove(lastEntry.getBucket());
lastEntry = null; lastEntry = null;
modCount++; knownMod++;
knownMod = modCount;
} }
}; };
} }
...@@ -293,7 +324,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -293,7 +324,7 @@ public class WeakHashMap extends AbstractMap implements Map
* number. <br> * number. <br>
* *
* It would be cleaner to have a WeakReference as field, instead of * 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 * the weak reference (by queue.poll) and wouldn't know where to
* look for this reference in the hashtable, to remove that entry. * look for this reference in the hashtable, to remove that entry.
* *
...@@ -329,6 +360,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -329,6 +360,7 @@ public class WeakHashMap extends AbstractMap implements Map
* Creates a new bucket for the given key/value pair and the specified * Creates a new bucket for the given key/value pair and the specified
* slot. * slot.
* @param key the key * @param key the key
* @param queue the queue the weak reference belongs to
* @param value the value * @param value the value
* @param slot the slot. This must match the slot where this bucket * @param slot the slot. This must match the slot where this bucket
* will be enqueued. * will be enqueued.
...@@ -346,7 +378,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -346,7 +378,7 @@ public class WeakHashMap extends AbstractMap implements Map
* current bucket. It also keeps a strong reference to the * current bucket. It also keeps a strong reference to the
* key; bad things may happen otherwise. * key; bad things may happen otherwise.
*/ */
class Entry implements Map.Entry class WeakEntry implements Map.Entry
{ {
/** /**
* The strong ref to the key. * The strong ref to the key.
...@@ -355,14 +387,16 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -355,14 +387,16 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Creates a new entry for the key. * 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. * Returns the underlying bucket.
* @return the owning bucket
*/ */
public WeakBucket getBucket() public WeakBucket getBucket()
{ {
...@@ -371,6 +405,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -371,6 +405,7 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Returns the key. * Returns the key.
* @return the key
*/ */
public Object getKey() public Object getKey()
{ {
...@@ -379,6 +414,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -379,6 +414,7 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Returns the value. * Returns the value.
* @return the value
*/ */
public Object getValue() public Object getValue()
{ {
...@@ -388,6 +424,8 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -388,6 +424,8 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* This changes the value. This change takes place in * This changes the value. This change takes place in
* the underlying hash map. * the underlying hash map.
* @param newVal the new value
* @return the old value
*/ */
public Object setValue(Object newVal) public Object setValue(Object newVal)
{ {
...@@ -398,50 +436,56 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -398,50 +436,56 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* The hashCode as specified in the Entry interface. * The hashCode as specified in the Entry interface.
* @return the hash code
*/ */
public int hashCode() public int hashCode()
{ {
return (key == NULL_KEY ? 0 : key.hashCode()) return key.hashCode() ^ WeakHashMap.hashCode(value);
^ (value == null ? 0 : value.hashCode());
} }
/** /**
* The equals method as specified in the Entry interface. * 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) public boolean equals(Object o)
{ {
if (o instanceof Map.Entry) if (o instanceof Map.Entry)
{ {
Map.Entry e = (Map.Entry) o; Map.Entry e = (Map.Entry) o;
return (key == NULL_KEY return key.equals(e.getKey())
? e.getKey() == null : key.equals(e.getKey())) && WeakHashMap.equals(value, e.getValue());
&& (value == null
? e.getValue() == null : value.equals(e.getValue()));
} }
return false; return false;
} }
public String toString()
{
return key + "=" + value;
}
} }
/** /**
* This returns the entry stored in this bucket, or null, if the * This returns the entry stored in this bucket, or null, if the
* bucket got cleared in the mean time. * 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) if (key == null)
return null; return null;
return new Entry(key); return new WeakEntry(key);
} }
} }
/** /**
* The entry set returned by <code>entrySet()</code>. * 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; private WeakBucket[] buckets;
...@@ -458,6 +502,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -458,6 +502,7 @@ public class WeakHashMap extends AbstractMap implements Map
* Creates a new weak hash map with default load factor and the given * Creates a new weak hash map with default load factor and the given
* capacity. * capacity.
* @param initialCapacity the initial capacity * @param initialCapacity the initial capacity
* @throws IllegalArgumentException if initialCapacity is negative
*/ */
public WeakHashMap(int initialCapacity) public WeakHashMap(int initialCapacity)
{ {
...@@ -469,10 +514,13 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -469,10 +514,13 @@ public class WeakHashMap extends AbstractMap implements Map
* load factor. * load factor.
* @param initialCapacity the initial capacity. * @param initialCapacity the initial capacity.
* @param loadFactor the load factor (see class description of HashMap). * @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) 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(); throw new IllegalArgumentException();
this.loadFactor = loadFactor; this.loadFactor = loadFactor;
threshold = (int) (initialCapacity * loadFactor); threshold = (int) (initialCapacity * loadFactor);
...@@ -482,7 +530,23 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -482,7 +530,23 @@ public class WeakHashMap extends AbstractMap implements Map
} }
/** /**
* 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) private int hash(Object key)
{ {
...@@ -498,7 +562,8 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -498,7 +562,8 @@ public class WeakHashMap extends AbstractMap implements Map
* Currently the iterator maintains a strong reference to the key, so * Currently the iterator maintains a strong reference to the key, so
* that is no problem. * that is no problem.
*/ */
private void cleanQueue() // Package visible for use by nested classes.
void cleanQueue()
{ {
Object bucket = queue.poll(); Object bucket = queue.poll();
while (bucket != null) while (bucket != null)
...@@ -521,8 +586,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -521,8 +586,7 @@ public class WeakHashMap extends AbstractMap implements Map
threshold = (int) (newsize * loadFactor); threshold = (int) (newsize * loadFactor);
buckets = new WeakBucket[newsize]; 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++) for (int i = 0; i < oldBuckets.length; i++)
{ {
WeakBucket bucket = oldBuckets[i]; WeakBucket bucket = oldBuckets[i];
...@@ -534,15 +598,15 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -534,15 +598,15 @@ public class WeakHashMap extends AbstractMap implements Map
Object key = bucket.get(); Object key = bucket.get();
if (key == null) if (key == null)
{ {
/* This bucket should be removed; it is probably // This bucket should be removed; it is probably
* already on the reference queue. We don't insert it // already on the reference queue. We don't insert it
* at all, and mark it as cleared. */ // at all, and mark it as cleared.
bucket.slot = -1; bucket.slot = -1;
size--; size--;
} }
else else
{ {
/* add this bucket to its new slot */ // Add this bucket to its new slot.
int slot = hash(key); int slot = hash(key);
bucket.slot = slot; bucket.slot = slot;
bucket.next = buckets[slot]; bucket.next = buckets[slot];
...@@ -556,10 +620,10 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -556,10 +620,10 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Finds the entry corresponding to key. Since it returns an Entry * Finds the entry corresponding to key. Since it returns an Entry
* it will also prevent the key from being removed under us. * it will also prevent the key from being removed under us.
* @param key the key. It may be null. * @param key the key, may be null
* @return The WeakBucket.Entry or null, if the key wasn't found. * @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) if (key == null)
key = NULL_KEY; key = NULL_KEY;
...@@ -567,7 +631,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -567,7 +631,7 @@ public class WeakHashMap extends AbstractMap implements Map
WeakBucket bucket = buckets[slot]; WeakBucket bucket = buckets[slot];
while (bucket != null) while (bucket != null)
{ {
WeakBucket.Entry entry = bucket.getEntry(); WeakBucket.WeakEntry entry = bucket.getEntry();
if (entry != null && key.equals(entry.key)) if (entry != null && key.equals(entry.key))
return entry; return entry;
...@@ -601,13 +665,12 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -601,13 +665,12 @@ public class WeakHashMap extends AbstractMap implements Map
{ {
int slot = bucket.slot; int slot = bucket.slot;
if (slot == -1) if (slot == -1)
/* this bucket was already removed. */ // This bucket was already removed.
return; return;
/* mark the bucket as removed. This is necessary, since the // Mark the bucket as removed. This is necessary, since the
* bucket may be enqueued later by the garbage collection and // bucket may be enqueued later by the garbage collection, and
* internalRemove, will be called a second time. // internalRemove will be called a second time.
*/
bucket.slot = -1; bucket.slot = -1;
if (buckets[slot] == bucket) if (buckets[slot] == bucket)
buckets[slot] = bucket.next; buckets[slot] = bucket.next;
...@@ -629,7 +692,7 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -629,7 +692,7 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Returns the size of this hash map. Note that the size() may shrink * 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. * @return the number of entries in this hash map.
*/ */
public int size() public int size()
...@@ -651,8 +714,9 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -651,8 +714,9 @@ public class WeakHashMap extends AbstractMap implements Map
/** /**
* Tells if the map contains the given key. Note that the result * Tells if the map contains the given key. Note that the result
* may change spontanously, if all the key was only weakly * may change spontanously, if the key was only weakly
* reachable. * reachable.
* @param key the key to look for
* @return true, iff the map contains an entry for the given key. * @return true, iff the map contains an entry for the given key.
*/ */
public boolean containsKey(Object key) public boolean containsKey(Object key)
...@@ -662,38 +726,38 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -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 * @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 * the key wasn't in this map, or if the mapped value was
* set to null. * explicitly set to null.
*/ */
public Object get(Object key) public Object get(Object key)
{ {
cleanQueue(); cleanQueue();
WeakBucket.Entry entry = internalGet(key); WeakBucket.WeakEntry entry = internalGet(key);
return entry == null ? null : entry.getValue(); return entry == null ? null : entry.getValue();
} }
/** /**
* Adds a new key/value mapping to this map. * Adds a new key/value mapping to this map.
* @param key the key. This may be null. * @param key the key, may be null
* @param value the value. This may be null. * @param value the value, may be null
* @return the value the key was mapped to previously. It returns * @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 * null if the key wasn't in this map, or if the mapped value
* explicitly set to null. * was explicitly set to null.
*/ */
public Object put(Object key, Object value) public Object put(Object key, Object value)
{ {
cleanQueue(); cleanQueue();
WeakBucket.Entry entry = internalGet(key); WeakBucket.WeakEntry entry = internalGet(key);
if (entry != null) if (entry != null)
return entry.setValue(value); return entry.setValue(value);
modCount++;
if (size >= threshold) if (size >= threshold)
rehash(); rehash();
internalAdd(key, value); internalAdd(key, value);
modCount++;
return null; return null;
} }
...@@ -702,17 +766,17 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -702,17 +766,17 @@ public class WeakHashMap extends AbstractMap implements Map
* @param key the key. This may be null. * @param key the key. This may be null.
* @return the value the key was mapped to previously. It returns * @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 * null if the key wasn't in this map, or if the mapped value was
* explicitly set to null. */ * explicitly set to null.
*/
public Object remove(Object key) public Object remove(Object key)
{ {
cleanQueue(); cleanQueue();
WeakBucket.Entry entry = internalGet(key); WeakBucket.WeakEntry entry = internalGet(key);
if (entry == null) if (entry == null)
{
return null; return null;
}
internalRemove(entry.getBucket());
modCount++; modCount++;
internalRemove(entry.getBucket());
return entry.getValue(); return entry.getValue();
} }
...@@ -722,10 +786,70 @@ public class WeakHashMap extends AbstractMap implements Map ...@@ -722,10 +786,70 @@ public class WeakHashMap extends AbstractMap implements Map
* silently removed. The returned set has therefore the same * silently removed. The returned set has therefore the same
* strange behaviour (shrinking size(), disappearing entries) as * strange behaviour (shrinking size(), disappearing entries) as
* this weak hash map. * this weak hash map.
* @return a set representation of the entries. */ * @return a set representation of the entries.
*/
public Set entrySet() public Set entrySet()
{ {
cleanQueue(); cleanQueue();
return theEntrySet; 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();
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment