Java Collections Framework: Section 1. Tutorial Tips
Java Collections Framework: Section 1. Tutorial Tips
Java Collections Framework: Section 1. Tutorial Tips
10 Apr 2001
This tutorial takes you on an extended tour of the Java™ Collections Framework. It
starts with a few simple programming examples for beginners and experts alike, to
get started with the Collections Framework quickly. The tutorial continues with a
discussion of sets and maps, their properties, and how their mathematical definition
differs from the Set, Map, and Collection definitions within the Collections
Framework. A section on the history of Java Collections Framework clears up some
of the confusion around the proliferation of set- and map-like classes.
with an introduction of JGL, a widely used algorithm and data structure library from
ObjectSpace that predates the Java Collections Framework.
Concepts
By the end of this tutorial, you will know how to do the following:
Contact
jGuru has been dedicated to promoting the growth of the Java technology
community through evangelism, education, and software since 1995. You can find
out more about their activities, including their huge collection of FAQs at jGuru.com.
To send feedback to jGuru about this tutorial, send mail to producer@jguru.com.
Introduction
This tutorial takes you on an extended tour of the Collections Framework, first
introduced with the Java 2 platform, Standard Edition, version 1.2. The Collections
Framework provides a well-designed set of interfaces and classes for storing and
manipulating groups of data as a single unit, a collection. The framework provides a
convenient API to many of the abstract data types familiar from computer science
data structure curriculum: maps, sets, lists, trees, arrays, hashtables, and other
collections. Because of their object-oriented design, the Java classes in the
Collections Framework encapsulate both the data structures and the algorithms
One thing worth noting early on is that while the framework is included with the Java
2 platform, a subset form is available for use with Java 1.1 run-time environments.
The framework subset is discussed in Working with the Collections Framework
support in JDK 1.1.
Before diving into the Collections Framework, it helps to understand some of the
terminology and set theory involved when working with the framework.
Mathematical background
In common usage, a collection is the same as the intuitive, mathematical concept of
a set. A set is just a group of unique items, meaning that the group contains no
duplicates. The Collections Framework, in fact, includes a Set interface, and a
number of concrete Set classes. But the formal notion of a set predates Java
technology by a century, when the British mathematician George Boole defined it in
formal logic. Most people learned some set theory in elementary school when
introduced to "set intersection" and "set union" through the familiar Venn Diagrams:
Also because they are sets, maps can be finite or infinite. An example of an infinite
map is the conversion from base 2 to base 10. Unfortunately, the Collections
Framework does not support infinite maps -- sometimes a mathematical function,
formula, or algorithm is preferred. But when a problem can be solved with a finite
map, the Collections Framework provides the Java programmer with a useful API.
Because the Collections Framework has formal definitions for the classes Set,
Collection, and Map, you'll notice the lower case words set, collection, and map
to distinguish the implementation from the concept.
Introduction
Now that you have some set theory under your belt, you should be able to
understand the Collections Framework more easily. The Collections Framework is
made up of a set of interfaces for working with groups of objects. The different
interfaces describe the different types of groups. For the most part, once you
understand the interfaces, you understand the framework. While you always need to
create specific implementations of the interfaces, access to the actual collection
should be restricted to the use of the interface methods, thus allowing you to change
the underlying data structure, without altering the rest of your code. The following
diagrams shows the framework interface hierarchy.
One might think that Map would extend Collection. In mathematics, a map is just
a collection of pairs. In the Collections Framework, however, the interfaces Map and
Collection are distinct with no lineage in the hierarchy. The reasons for this
distinction have to do with the ways that Set and Map are used in the Java libraries.
The typical application of a Map is to provide access to values stored by keys. The
set of collection operations are all there, but you work with a key-value pair instead
of an isolated element. Map is therefore designed to support the basic operations of
get() and put() , which are not required by Set. Moreover, there are methods
that return Set views of Map objects:
If you are moving from the historical collection classes to the new framework
classes, one of the primary differences is that all operations are unsynchronized with
the new classes. While you can add synchronization to the new classes, you cannot
remove it from the old.
Collection interface
The Collection interface is used to represent any group of objects, or elements.
You use the interface when you wish to work with a group of elements in as general
a manner as possible. Here is a list of the public methods of Collection in Unified
Modeling Language (UML) notation.
The interface supports basic operations like adding and removing. When you try to
remove an element, only a single instance of the element in the collection is
removed, if present.
• int size()
• boolean isEmpty()
• boolean contains(Object element)
• Iterator iterator()
Iterator interface
Other operations the Collection interface supports are tasks done on groups of
elements or the entire collection at once:
The remaining two interface methods, which convert a Collection to an array, will
be discussed in Converting from new collections to historical collections.
AbstractCollection class
The AbstractCollection class provides the basis for the concrete collections
framework classes. While you are free to implement all the methods of the
In the creation of the Collections Framework, the Sun development team needed to
provide flexible interfaces that manipulated groups of elements. To keep the design
simple, instead of providing separate interfaces for optional capabilities, the
interfaces define all the methods an implementation class may provide. However,
some of the interface methods are optional. Because an interface implementation
must provide implementations for all the interface methods, there needed to be a
way for a caller to know if an optional method is not supported. The manner the
framework development team chose to signal callers when an optional method is
called was to throw an UnsupportedOperationException. If in the course of
using a collection an UnsupportedOperationException is thrown, then the
operation failed because it is not supported. To avoid having to place all collection
operations within a try-catch block, the UnsupportedOperationException
class is an extension of the RuntimeException class.
In addition to handling optional operations with a run-time exception, the iterators for
the concrete collection implementations are fail-fast. That means that if you are
using an Iterator to traverse a collection while the underlying collection is being
modified by another thread, then the Iterator fails immediately by throwing a
ConcurrentModificationException (another RuntimeException). That
means the next time an Iterator method is called, and the underlying collection
has been modified, the ConcurrentModificationException exception gets
thrown.
Set interface
The Set interface extends the Collection interface and, by definition, forbids
duplicates within the collection. All the original methods are present and no new
methods are introduced. The concrete Set implementation classes rely on the
equals() method of the object added to check for equality.
To optimize HashSet space usage, you can tune the initial capacity and load factor.
The TreeSet has no tuning options, as the tree is always balanced, ensuring
log(n) performance for insertions, deletions, and queries.
To demonstrate the use of the concrete Set classes, the following program creates
a HashSet and adds a group of names, including one name twice. The program
then prints out the list of names in the set, demonstrating the duplicate name isn't
present. Next, the program treats the set as a TreeSet and displays the list sorted.
import java.util.*;
same size and contain the same elements. By definition, the hash code for a set is
the sum of the hash codes for the elements of the set. Thus, no matter what the
internal ordering of the sets, two equal sets will report the same hash code.
Exercises
List interface
The List interface extends the Collection interface to define an ordered
collection, permitting duplicates. The interface adds position-oriented operations, as
well as the ability to work with just a part of the list.
• ListIterator listIterator()
• ListIterator listIterator(int startIndex)
• List subList(int fromIndex, int toIndex)
In working with subList() , it is important to mention that the element at
fromIndex is in the sublist, but the element at toIndex is not. This loosely maps
to the following for-loop test cases:
ListIterator interface
The following source code demonstrates looping backwards through a list. Notice
that the ListIterator is originally positioned beyond the end of the list
(list.size()), as the index of the first element is 0.
The add() operation requires a little bit of explanation also. Adding an element
results in the new element being added immediately prior to the implicit cursor.
Thus, calling previous() after adding an element would return the new element
and calling next() would have no effect, returning what would have been the next
element prior to the add operation.
ends of the list (only the new methods are shown in the following diagram):
By using these new methods, you can easily treat the LinkedList as a stack,
queue, or other end-oriented data structure.
The following program demonstrates the use of the concrete List classes. The first
part creates a List backed by an ArrayList. After filling the list, specific entries
are retrieved. The LinkedList part of the example treats the LinkedList as a
queue, adding things at the beginning of the queue and removing them from the
end.
import java.util.*;
}
Running the program produces the following output. Notice that unlike Set, List
permits duplicates.
AbstractList AbstractSequentialList
unmodifiable
Object get(int index) ListIterator listIterator(int index)
int size() - boolean hasNext()
- Object next()
- int nextIndex()
- boolean hasPrevious()
- Object previous()
- int previousIndex()
int size()
modifiable
Object get(int index) ListIterator listIterator(int index)
int size() - boolean hasNext()
Object set(int index, Object- element)
Object next()
- int nextIndex()
- boolean hasPrevious()
- Object previous()
- int previousIndex()
int size()
ListIterator
- set(Object element)
As the Collection interface documentation states, you should also provide two
constructors, a no-argument one and one that accepts another Collection.
Exercise
Map interface
The Map interface is not an extension of the Collection interface. Instead, the
interface starts off its own interface hierarchy for maintaining key-value associations.
The interface describes a mapping from keys to values, without duplicate keys, by
definition.
The interface methods can be broken down into three sets of operations: altering,
querying, and providing alternative views.
The alteration operations allow you to add and remove key-value pairs from the
map. Both the key and value can be null. However, you should not add a Map to
itself as a key or value.
Map.Entry interface
The entrySet() method of Map returns a collection of objects that implement the
Map.Entry interface. Each object in the collection is a specific key-value pair in the
underlying Map.
Iterating through this collection, you can get the key or value, as well as change the
value of each entry. However, the set of entries becomes invalid, causing the iterator
behavior to be undefined, if the underlying Map is modified outside the setValue()
method of the Map.Entry interface.
To optimize HashMap space usage, you can tune the initial capacity and load factor.
The TreeMap has no tuning options, as the tree is always balanced.
The following program demonstrates the use of the concrete Map classes. The
program generates a frequency count of words passed from the command line. A
HashMap is initially used for data storage. Afterwards, the map is converted to a
TreeMap to display the key list sorted.
import java.util.*;
Unsorted:
and sorted:
AbstractMap class
WeakHashMap class
The Java 2 SDK, Standard Edition, version 1.3 adds a constructor to WeakHashMap
that accepts a Map. With version 1.2 of the Java 2 platform, the available
constructors permit only overriding the default load factor and initial capacity setting,
not initializing the map from another map (as recommended by the Map interface
documentation).
Sorting
There have been many changes to the core Java libraries to add support for sorting
with the addition of the Collections Framework to the Java 2 SDK, version 1.2.
Classes like String and Integer now implement the Comparable interface to
provide a natural sorting order. For those classes without a natural order, or when
you desire a different order than the natural order, you can implement the
Comparator interface to define your own.
Comparable interface
The Comparable interface, in the java.lang package, is for when a class has a
natural ordering. Given a collection of objects of the same type, the interface allows
you to order the collection into that natural ordering.
The compareTo() method compares the current instance with an element passed
in as an argument. If the current instance comes before the argument in the
ordering, a negative value is returned. If the current instance comes after, then a
positive value is returned. Otherwise, zero is returned. It is not a requirement that a
zero return value signifies equality of elements. A zero return value just signifies that
two objects are ordered at the same position.
There are fourteen classes in the Java 2 SDK, version 1.2 that implement the
Comparable interface. The following table shows their natural ordering. While some
classes share the same natural ordering, you can sort only classes that are mutually
comparable.
Class Ordering
BigDecimal, BigInteger, Byte, Numerical
Double, Float, Integer, Long, Short
Character Numerical by Unicode value
CollationKey Locale-sensitive string ordering
Date Chronological
File Numerical by Unicode value of characters in
fully-qualified, system-specific pathname
ObjectStreamField Numerical by Unicode value of characters in
name
String Numerical by Unicode value of characters in
string
The documentation for the compareTo() method of String defines the ordering
lexicographically. This means the comparison is of the numerical values of the
characters in the text, which is not necessarily alphabetically in all languages. For
locale-specific ordering, use Collator with CollationKey.
import java.text.*;
import java.util.*;
Comparator interface
The return values of the compare() method of Comparator are similar to the
compareTo() method of Comparable. In this case, if the first element comes
before the second element in the ordering, a negative value is returned. If the first
element comes after, then a positive value is returned. Otherwise, zero is returned.
Similar to Comparable, a zero return value does not signify equality of elements. A
zero return value just signifies that two objects are ordered at the same position. It's
up to the user of the Comparator to determine how to deal with it. If two unequal
elements compare to zero, you should first be sure that is what you want and
second document the behavior.
To demonstrate, you may find it easier to write a new Comparator that ignores
case, instead of using Collator to do a locale-specific, case-insensitive
comparison. The following is one such implementation:
The Collections class has one predefined Comparator available for reuse.
Calling Collections.reverseOrder() returns a Comparator that sorts objects
that implement the Comparable interface in reverse order.
Exercise
The interface provides access methods to the ends of the set as well as to subsets
of the set. When working with subsets of the list, changes to the subset are reflected
in the source set. In addition, changes to the source set are reflected in the subset.
This works because subsets are identified by elements at the end points, not
indices. In addition, if the fromElement is part of the source set, it is part of the
subset. However, if the toElement is part of the source set, it is not part of the
subset. If you would like a particular to-element to be in the subset, you must find the
next element. In the case of a String, the next element is the same string with a
null character appended (string+"\0").;
The Collections Framework provides a special Map interface for maintaining keys in
a sorted order: SortedMap.
The interface provides access methods to the ends of the map as well as to subsets
of the map. Working with a SortedMap is just like a SortedSet, except the sort is
done on the map keys. The implementation class provided by the Collections
Framework is a TreeMap.
Because maps can only have one value for every key, if comparing two keys when
adding a key-value pair results in a zero return value (from either the compareTo()
method of Comparable or the compare() method of Comparator), then the value
for the original key is replaced with the new value. If the elements are equal, then
that is okay. However, if they are not, then you should modify the comparison
method such that the comparison is compatible with equals().
Introduction
To keep the Collections Framework simple, added functionality is provided by
wrapper implementations (also known as the Decorator design pattern -- see the
Design Patterns book for more information on patterns). These wrappers delegate
the collections part to the underlying implementation class, but they add functionality
on top of the collection. These wrappers are all provided through the Collections
class. The Collections class also provides support for creating special-case
collections.
Read-only collections
After you've added all the necessary elements to a collection, it may be convenient
to treat that collection as read-only, to prevent the accidental modification of the
collection. To provide this capability, the Collections class provides six factory
methods, one for each of Collection, List, Map, Set, SortedMap, and
SortedSet.
• Collection unmodifiableCollection(Collection
collection)
• List unmodifiableList(List list)
• Map unmodifiableMap(Map map)
• Set unmodifiableSet(Set set)
import java.util.*;
Thread-safe collections
The key difference between the historical collection classes and the new
implementations within the Collections Framework is the new classes are not
thread-safe. The designers took this approach to allow you to use synchronization
only when you need it, making everything work much faster. If, however, you are
using a collection in a multi-threaded environment, where multiple threads can
modify the collection simultaneously, the modifications need to be synchronized. The
Collections class provides for the ability to wrap existing collections into
synchronized ones with another set of six methods:
• Collection synchronizedCollection(Collection
collection)
• List synchronizedList(List list)
• Map synchronizedMap(Map map)
• Set synchronizedSet(Set set)
Singleton collections
The Collections class provides for the ability to create single element sets fairly
easily. Instead of having to create the Set and fill it in in separate steps, you can do
it all at once. The resulting Set is immutable.
Empty collections
• List EMPTY_LIST
• Set EMPTY_SET
The Java 2 SDK, Standard Edition, version 1.3 has a predefined empty map
constant:
• Map EMPTY_MAP
Introduction
While this tutorial is about the new Collections Framework of the Java 2 SDK, there
are times when you still need to use some of the original collections capabilities.
This section reviews some of the capabilities of working with arrays, vectors,
hashtables, enumerations, and other historical capabilities.
Arrays
One learns about arrays fairly early on when learning the Java programming
language. Arrays are defined to be fixed-size collections of the same datatype. They
are the only collection that supports storing primitive datatypes. Everything else,
including arrays, can store objects. When creating an array, you specify both the
number and type of object you wish to store. And, over the life of the array, it can
neither grow nor store a different type (unless it extends the first type).
To find out the size of an array, you ask its single public instance variable, length,
as in array.length.
To access a specific element, either for setting or getting, you place the integer
argument within square brackets ([int]), either before or after the array reference
variable. The integer index is zero-based, and accessing beyond either end of the
array will throw an ArrayIndexOutOfBoundsException at run time. If, however,
you use a long variable to access an array index, you'll get a compiler-time error.
To make a copy of an array, perhaps to make it larger, you use the arraycopy()
method of System. You need to preallocate the space in the destination array.
When transitioning from Vector to ArrayList, one key difference is that the
arguments have been reversed to positionally change an element's value:
Enumeration interface
The Enumeration interface allows you to iterate through all the elements of a
collection. In the Collections Framework, this interface has been superceded by the
Iterator interface. However, not all libraries support the newer interface, so you
may find yourself using Enumeration from time to time.
BitSet class
A BitSet represents an alternate representation of a set. Given a finite number of n
objects, you can associate a unique integer with each object. Then each possible
subset of the objects corresponds to an n-bit vector, with each bit "on" or "off"
depending on whether the object is in the subset. For small values of n, a bit vector
might be an extremely compact representation. However, for large values of n, an
actual bit vector might be inefficient, when most of the bits are off.
Exercise
Introduction
The Collections and Arrays classes, available as part of the Collections
Framework, provide support for various algorithms with the collection classes, both
new and old. The different operations, starting with sorting and searching, are
described next.
Sorting arrays
While the TreeSet and TreeMap classes offer sorted version of sets and maps,
there is no sorted List collection implementation. Also, prior to the collections
framework, there was no built-in support for sorting arrays. As part of the framework,
you get both support for sorting a List, as well as support for sorting arrays of
anything, including primitives. With any kind of sorting, all items must be comparable
to each other (mutually comparable). If they are not, a ClassCastException will
be thrown.
Sorting of a List is done with one of two sort() methods in the Collections
class. If the element type implements Comparable then you would use the
sort(List list) version. Otherwise, you would need to provide a Comparator
and use sort(List list, Comparator comparator). Both versions are
destructive to the List and guarantee O(n log 2 n) performance (or better),
including when sorting a LinkedList, using a merge sort variation.
Sorting of arrays is done with one of eighteen different methods. There are two
methods for sorting each of the seven primitive types (except boolean), one for
sorting the whole array and one for sorting part of the array. The remaining four are
for sorting object arrays (Object[ ]).
To sort primitive arrays, simply call Arrays.sort() with your array as the
argument and let the compiler determine which of the following methods to pick:
Searching
Besides sorting, the Collections and Arrays classes provide mechanisms to
search a List or array, as well as to find the minimum and maximum values within
a Collection.
While you can use the contains() method of List to find if an element is part of
the list, it assumes an unsorted list. If you've previously sorted the List, using
Collections.sort(), then you can do a much quicker binary search using one
of the two overridden binarySearch() methods. If the objects in the List
implement Comparable, then you don't need to provide a Comparator. Otherwise,
you must provide a Comparator. In addition, if you sorted with a Comparator, you
must use the same Comparator when binary searching.
Array searching works the same way. After using one of the Arrays.sort()
methods, you can take the resulting array and search for an element. There are
seven overridden varieties of binarySearch() to search for a primitive (all but
boolean), and two to search an Object array, both with and without a
Comparator.
Besides searching for specific elements within a List, you can search for extreme
elements within any Collection: the minimum and maximum. If you know your
collection is already sorted, just get the first or last element. However, for unsorted
collections, you can use one of the min() or max() methods of Collections. If
the object in the collection doesn't implement Comparable, then you must provide a
Comparator.
Checking equality
While the MessageDigest class always provided an isEqual() method to
compare two byte arrays, it never felt right to use it to compare byte arrays unless
they were from message digests. Now, with the help of the Arrays class, you can
check for equality of any array of primitive or object type. Two arrays are equal if
they contain the same elements in the same order. Checking for equality with arrays
of objects relies on the equals() method of each object to check for equality.
Manipulating elements
The Collections and Arrays classes offer several ways of manipulating the
elements within a List or array. There are no additional ways to manipulate the
other key framework interfaces (Set and Map).
With a List, the Collections class lets you replace every element with a single
element, copy an entire list to another, reverse all the elements, or shuffle them
around. When copying from one list to another, if the destination list is larger, the
remaining elements are untouched.
Big-O notation
Performance of sorting and searching operations with collections of size n is
measured using Big-O notation. The notation describes the complexity of the
algorithm in relation to the maximum time in which an algorithm operates, for large
values of n. For instance, if you iterate through an entire collection to find an
element, the Big-O notation is referred to as O(n), meaning that as n increases,
time to find an element in a collection of size n increases linearly. This demonstrates
that Big-O notation assumes worst case performance. It is always possible that
performance is quicker.
The following table shows the Big-O values for different operations, with 65,536 as
the value for n. In addition, the operation count shows that if you are going to
perform multiple search operations on a collection, it is faster to do a quick sort on
the collection, prior to searching, versus doing a linear search each time. (And, one
should avoid bubble sorting, unless n is really small!)
Legend: n = 65536
Introduction
The Collections Framework was designed such that the new framework classes and
the historical data structure classes can interoperate. While it is good if you can have
all your new code use the new framework, sometimes you can't. The framework
provides much support for intermixing the two sets of collections. In addition, you
can develop with a subset of the capabilities with JDK 1.1.
For going from any array to a List, you use the asList(Object array[])
method of the Arrays class. Changes to the List pass through to the array, but
you cannot adjust the size of the array.
There are two ways to go from Collection to array, depending upon the type of
array you need. The simplest way involves going to an Object array. In addition,
you can also convert the collection into any other array of objects. However, you
cannot directly convert the collection into an array of primitives, as collections must
hold objects.
The following table lists the classes available in the Collections Framework release
for JDK 1.1. In some cases, there will be two different implementations of the same
class, like with Vector, as the 1.2 framework version implements List and the
core 1.1 version doesn't.
AbstractCollection AbstractList
AbstractMap AbstractSequentialList
AbstractSet ArrayList
Arrays Collection
Collections Comparable
Comparator ConcurrentModificationException
HashMap HashSet
Hashtable Iterator
LinkedList List
ListIterator Map
NoSuchElementException Random
Set SortedMap
SortedSet TreeMap
TreeSet UnsupportedOperationException
Vector
Introduction
Because the Collections Framework was not available prior to the introduction of the
Java 2 platform, several alternative collection libraries became available. Two such
libraries are Doug Lea's Collections Package and ObjectSpace's JGL.
ObjectSpace's JGL
In addition to Doug Lea's collections library, the Generic Collection Library for Java
(JGL) from ObjectSpace was an early collection library available for the Java
platform. (If you are curious of how the library name maps to the acronym, it doesn't.
The name of the first version of the library infringed on Sun's Java trademark.
ObjectSpace changed the name, but the original acronym stuck.) Following the
design patterns of the Standard Template Library (STL) for C++, the library provides
algorithmic support, in addition to a data structure library. While the JGL is a good
alternative collection framework, it didn't meet the design goals of the Collections
Framework team: "The main design goal was to produce an API that was reasonably
small, both in size and, more importantly, in conceptual weight." With that in mind,
the team came up with the Collections Framework.
While not adopted by Sun Microsystems, the JGL has been included with many IDE
tools. Due to its early availability, the JGL is available to well over 100,000
developers.
For a comparison of JGL versus the Collections Framework, see The battle of the
container frameworks: which should you use? article in JavaWorld.
Section 9. Exercises
Each exercise has a list of any prerequisite exercises, a list of skeleton code for you
to start with, links to necessary API pages, and a text description of the exercise
goal. In addition, there is help for each task and a solutions page with links to files
that comprise a solution to the exercise.
are off (or false). For maintaining these sparsely populated sets, the BitSet class
can be very inefficient. Because the majority of the bits will be off, space will be
occupied to store nothing. For working with these sparse bitsets, you can create an
alternate representation, backed instead by a hashtable, or HashMap. Only those
positions where a value is set are then stored in the mapping.
To create a sparse bitset, subclass BitSet and override the necessary methods
(everything). The skeleton code should help you get started, so you can focus on the
set-oriented routines.
Skeleton Code
• SparseBitSet.java
• Tester.java
Task 1:
Either start with the skeleton code or create a SparseBitSet class. The skeleton
provides a no-argument constructor only. Because the bitmap will be sparse, you
shouldn't provide a constructor that will preallocate any space, as BitMap does.
Besides a constructor, the skeleton defines the clear(), clone(), equals(),
get(), hashCode(), set(), size(), and toSting() method.
In the skeleton, the getBitSet() method returns the internal Set used to store the
bits. You should use this method as you complete the other methods in the
subclass. The actual HashSet used to store the bit values is created for you in the
constructor.
Task 2:
Working alphabetically, the first method to complete is the and(BitSet set)
method. This method performs a logical AND of the two bit sets. Only those bits in
both sets are in the resulting set. Complete the and() method to combine the
internal Set with that of the argument.
Task 3:
The next method to complete is the andNot(BitSet set) method. Instead of
keeping bits present in both, the andNot() operation will remove bits from the
current set that are also in the set passed as an argument. This is sometimes called
a logical NAND operation.
Task 4:
Because the clear(), clone(), equals(), get(), and hashCode() methods
are defined in the skeleton code, the next method to complete is the length()
method. The length() method returns the logical size of the BitSet, which is
defined to be the position of the highest set bit, plus one. Thus, if bit 127 was set, the
length would be 128 as the bit counting starts at zero.
Task 5:
The last easy method to complete is the or(BitSet set) method. This method
performs a logical OR operation of the two bit sets. Every bit set of either set is in the
resulting set.
sets.
Task 6:
With the set(), size(), and toString() methods already defined for you, you're
left to complete the xor(BitSet set) method. This method performs a logical
exclusive or (XOR) operation. Only those bits on in one of the sets will be on in the
resulting set.
Unlike the other operations, the solution is not just a single method call of Set.
Task 7:
Compile your program and run the Tester program to see what happens. The
Tester program creates a couple of sets and performs all the operations.
• Solution/SparseBitSet.java
• Solution/Tester.java
If you aren't familiar with the Swing component set, don't worry. The Tester
program includes all the necessary code to create the user interface. You are only
responsible for finishing up the data model implementation and adding some action
behind some of the buttons in the user interface.
Skeleton Code
• SortedListModel.java
• Tester.java
Task 1:
Either start with the skeleton code or create a SortedListModel class. The class
extends AbstractListModel .
Task 2:
Create an instance variable of type SortedSet . Then, in the constructor create an
instance of type TreeSet and assign it to the variable.
Task 3:
At a minimum, the AbstractListModel class requires the getSize() and
getElementAt() methods of the ListModel interface to be defined. Complete
the stubs such that they get the size and element from the set saved in the prior
step.
Task 4:
Besides implementing the methods of ListModel , the SortedListModel class
provides several methods to access and alter the data model. Many of the methods
are already completed. The following UML diagram shows the complete set of
operations.
Task 5:
Two methods in the SortedListModel skeleton are left to complete:
firstElement() and lastElement(). These require the use of methods
specific to the SortedSet interface to complete.
Task 6:
In the Tester skeleton, the printAction() method needs to be completed. As
the name may imply, its purpose is to display a list of the elements in the JList.
Use an Iterator to display the elements in its data model. The data model is
stored in the model variable, which is of type SortedListModel.
Task 7:
Compile your program and run the Tester program to see what happens. You can
provide several values as command line arguments to initialize the contents. Try out
several buttons on the user interface to make sure the SortedListModel works.
java Tester One Two Three Four Five Six Seven Eight
Nine Ten
• Solution/SortedListModel.java
• Solution/Tester.java
If you aren't familiar with the Swing component set, don't worry. The Tester
program includes all the necessary code to create the user interface. You are only
responsible for finishing up the data model implementation.
Skeleton Code
• ArrayListComboBoxModel.java
• Tester.java
Task 1:
Either start with the skeleton code or create an ArrayListComboBoxModel class.
The class extends AbstractListModel and implements
MutableComboBoxModel .
Task 2:
Create a variable of type List. Refer the List argument in the constructor to your
variable.
Task 3:
The AbstractListModel class leaves the getSize() and getElementAt()
methods of the ListModel interface to be defined. Complete the stubs such that
they get the size and element from the list saved in the prior step.
Task 4:
By stating that the ArrayListComboBoxModel class implements the
MutableComboBoxModel interface, you are saying you'll provide implementations
for the methods of both the MutableComboBoxModel and ComboBoxModel
interfaces, as the former extends the latter. The getSelectedItem() and
setSelectedItem() methods of the ComboBoxModel interface are already
defined for you.
Task 5:
The MutableComboBoxModel interface, defines four methods:
addElement(Object element) , insertElementAt(Object element,
int position) , removeElement(Object element) , and
removeElementAt(int position) . Complete the stubs such that they alter
the list saved in a prior step.
Task 6:
Compile your program and run the Tester program to see what happens. Provide
several names as command line arguments. The Tester program tests your new
data model class by adding and removing elements from the model.
Check your output to make sure that Jim, Joe, and Mary are added
to the names you provided.
• Solution/ArrayListComboBoxModel.java
• Solution/Tester.java
If you aren't familiar with the Swing component set, don't worry. The Tester
program includes all the necessary code to create the user interface. You are only
responsible for counting the words and formatting the output. Even the source code
to read from the URL is provided.
Skeleton Code
• CaseInsensitiveComparator.java
• Tester.java
• WordCount.java
Task 1:
Either start with the skeleton code or create a WordCount class.
Task 2:
Create an instance variable of type Map and assign it to a new HashMap . In the
getMap() method return the map created. In the clear() method, empty out the
defined map.
Task 3:
Complete the addWords() method to count each word returned by the
StringTokenizer . The program already separates each line in the URL into
individual words.
Task 4:
The Tester program has a JTextArea to display the results of the counting. The
program displays the String returned by the private convertMap() method in the
JTextArea. It is your job to format the output nicely, as the toString() of
AbstractMap displays everything on one line. Start off with the skeleton code for
CaseInsensitiveComparator so you can sort the output in a case-insensitive
manner. The implementation will be identical to the comparator interface described
in Sorting.
Task 5:
Now that you have a case-insensitive Comparator , use it to create a TreeMap
full of the original map contents. That way, the output can be displayed sorted.
Task 6:
After getting an Iterator of all the keys, display one key-value pair per line, using
the predefined PrintWriter to format the output. It is backed by a StringBuffer
and will be automatically returned.
Task 7:
Compile your program and run the Tester program to see what happens. You
specify the URL to read in the JTextField. When you press Enter, the URL is read
and the words are added to the map. Once done reading, the JTextArea is
updated. If you want to clear out the map, press the Clear button.
• Solution/CaseInsensitiveComparator.java
• Solution/Tester.java
• Solution/WordCount.java
Conclusion
The Collections Framework provides a well-designed set of interfaces,
implementations, and algorithms for representing and manipulating groups of
elements. Understanding all the capabilities of this framework reduces the effort
required to design and implement a comparable set of APIs, as was necessary prior
to their introduction. Now that you have completed this tutorial, you can effectively
manage groups of data elements.
Resources
Learn
• Read the "Java language essentials tutorial" on developerWorks.
• "How to Build Data Structures in Java", a JDC article from prior to the existence
of the Collections Framework.
• "Design Patterns" by Erich Gamma, Richard Helm, Ralph Johnson, and John
Vlissides (The Gang of Four).
• Collections Framework Support for JDK 1.1.
• Doug Lea's Collections Package.
• "The battle of the container frameworks: which should you use?", JavaWorld
article from January 1999.
Get products and technologies
• Sun's Collections Framework home page.
Discuss
• Participate in developerWorks blogs and get involved in the developerWorks
community.