Most Java programmers are familiar with the concept
of an iterator, which is an object that may be used to
traverse a sequence of elements, whether they are in a JGL container,
a JDK container, a native Java array, a Java stream, or another
kind of sequence. The JDK Enumeration
is a kind of iterator that is created by invoking elements()
on a JDK container.
When an Enumeration
is first created, it is positioned at the first element of the
container. As long as hasMoreElements()
returns true, it is safe to use nextElement()
to get the element that the Enumeration
is currently positioned
at and then advance the Enumeration
to the next element.
An Enumeration
is a limited form of iterator because it is read-only and can
only be advanced by one position at a time. JGL includes a more
powerful hierarchy of interfaces that embrace and extend Enumeration
,
as shown by the following diagram.
Here is a brief overview of each iterator interface.
Iterator Interface | Description |
InputIterator
| can read one item at a time, in a forward direction only |
OutputIterator
| can write one item at a time, in a forward direction only |
ForwardIterator
| combines characteristics of input and output iterators |
BidirectionalIterator
| like forward, plus the ability to move backwards |
RandomAccessIterator
| like bidirectional, plus the ability to jump by an arbitrary distance |
Each JGL container has an associated class of iterator. Here is
a table that lists every JGL container class in alphabetical order
together with its associated iterator and the interface that its
iterator implements.
Container | Associated Iterator | Iterator Interface |
Array
| ArrayIterator
| RandomAccessIterator
|
BooleanArray
| BooleanIterator
| RandomAccessIterator
|
ByteArray
| ByteIterator
| RandomAccessIterator
|
CharArray
| CharIterator
| RandomAccessIterator
|
Deque
| DequeIterator
| RandomAccessIterator
|
DoubleArray
| DoubleIterator
| RandomAccessIterator
|
FloatArray
| FloatIterator
| RandomAccessIterator
|
IntArray
| IntIterator
| RandomAccessIterator
|
DList
| DListIterator
| BidirectionalIterator
|
HashMap
| HashMapIterator
| ForwardIterator
|
ObjectArray
| ObjectIterator
| RandomAccessIterator
|
OrderedMap
| OrderedMapIterator
| BidirectionalIterator
|
OrderedSet
| OrderedSetIterator
| BidirectionalIterator
|
PriorityQueue
| ArrayIterator
| RandomAccessIterator
|
Queue
| Same as underlying sequence | Same as underlying sequence |
HashSet
| HashSetIterator
| ForwardIterator
|
ShortArray
| ShortIterator
| RandomAccessIterator
|
SList
| SListIterator
| ForwardIterator
|
Stack
| Same as underlying sequence | Same as underlying sequence |
VectorArray
| VectorIterator
| RandomAccessIterator
|
elements()
by returning an instance of their associated iterator class cast
to an Enumeration
and positioned at their first element. The following example uses
an Enumeration
to iterate through every element of an Array
.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import java.util.Enumeration;
public class Iterators1
{
public static void main( String[] args )
{
Array array = new Array();
array.add( "magical" );
array.add( "mystery" );
array.add( "tour" );
Enumeration iterator = array.elements();
while ( iterator.hasMoreElements() )
System.out.println( iterator.nextElement() );
}
}
Output
magical
mystery
tour
All JGL containers respond to start()
by returning an instance of their associated iterator class cast
to a ForwardIterator
and positioned at the first element. finish()
works in a similar fashion except that the iterator is positioned
immediately after the last element.
All JGL iterators may be compared using equals(),
cloned using the standard clone()
message, and advanced by one or more positions using advance().
You can read the object that an iterator is currently positioned
at by using get(),
and replace the object that an iterator is currently positioned
at by using put()
.
The following example uses these operations to convert every String
in an Array
and a DList
to uppercase.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
public class Iterators2
{
public static void main( String[] args )
{
Array array = new Array();
array.add( "magical" );
array.add( "mystery" );
array.add( "tour" );
System.out.println( "before array = " + array );
toUppercase( array );
System.out.println( "after array = " + array );
DList list = new DList();
list.add( "magical" );
list.add( "mystery" );
list.add( "tour" );
System.out.println( "before list = " + list );
toUppercase( list );
System.out.println( "after list = " + list );
}
static void toUppercase( Container container )
{
// Obtain iterator positioned at first element.
ForwardIterator iterator = container.start();
// Obtain iterator positioned immediately after last element.
ForwardIterator end = container.finish();
// Loop through every element.
while ( !iterator.equals( end ) )
{
String current = (String) iterator.get();
// Replace current element with uppercase equivalent.
iterator.put( current.toUpperCase() );
iterator.advance(); // Move forward by one element.
}
}
}
Output
before array = Array( magical, mystery, tour )
after array = Array( MAGICAL, MYSTERY, TOUR )
before list = DList( magical, mystery, tour )
after list = DList( MAGICAL, MYSTERY, TOUR )
In most cases, the functionality of an Enumeration
or a ForwardIterator
will suffice. However, there are occasions that require you to
use the full power of the container's native iterator type. For
example, a BidirectionalIterator
can also move backwards by one or more positions using retreat(),
and can get and put elements that are a specified distance (ahead or behind) from
their current position using a more powerful version of get()
and put()
.
All JGL containers respond to begin()
by returning an uncast instance of their associated iterator class
positioned at their first element. Similarly,
end()
returns an uncast instance of a
container's associated iterator class that is positioned immediately
after its last element.
The following example uses some of these more advanced
features to iterator backwards through an Array
.
Note that the same thing can be accomplished more easily by using
a reverse iterator, described later in this chapter.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
public class Iterators3
{
public static void main( String[] args )
{
Array array = new Array();
array.add( "ape" );
array.add( "giraffe" );
array.add( "lizard" );
System.out.println( "array = " + array );
// Obtain iterator positioned "just-past-the-end".
ArrayIterator iterator = array.end();
// Obtain iterator positioned at first element.
ArrayIterator begin = array.begin();
while ( !iterator.equals( begin ) )
{
iterator.retreat();
System.out.println( iterator.get() );
}
}
}
Output
array = Array( ape, giraffe, lizard )
lizard
giraffe
ape
All JGL algorithms can be easily applied to an entire
container. However, there are occasions where you might want to
apply an algorithm to a sub-range of a container. Most algorithms
therefore have a variation that allows you to specify a sub-range
by supplying an iterator positioned at the first element of the
range and another iterator positioned immediately after
the last element of the range. The following two lines of code
are therefore equivalent:
Sorting.sort( array );
Sorting.sort( array.begin(), array.end() );
The following example uses iterator-based versions
of the sort and replace algorithms to sort the first half of an
Array
and to replace every 7 in the last three elements with a 0.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.algorithms.*;
public class Iterators4
{
public static void main( String[] args )
{
Array array = new Array();
array.add( new Integer( 4 ) );
array.add( new Integer( 7 ) );
array.add( new Integer( 2 ) );
array.add( new Integer( 7 ) );
array.add( new Integer( 1 ) );
array.add( new Integer( 7 ) );
System.out.println( "array = " + array );
// Obtain iterator positioned at first element.
ArrayIterator first = array.begin();
// Obtain iterator positioned immediately after third element.
ArrayIterator last = array.begin();
last.advance( 3 );
// Sort first three elements.
Sorting.sort( first, last );
System.out.println( "array = " + array );
// Replace 7 with 0 in last three elements.
Replacing.replace( last, array.end(), new Integer( 7 ), new Integer( 0 ) );
System.out.println( "array = " + array );
}
}
Output
array = Array( 4, 7, 2, 7, 1, 7 )
array = Array( 2, 4, 7, 7, 1, 7 )
array = Array( 2, 4, 7, 0, 1, 0 )
All operations can be performed on JGL containers
without any knowledge of iterators. However, there are a few methods
that can operate more efficiently with iterators. For example,
let's say you wish to remove the first occurrence of a particular
object from a DList
.
One way to do this is to use indexOf()
to find the index of the first occurrence and then use index-based
version of remove()
to delete the node with that index. However, since a DList
is not random access, the index-based versions of the methods
can only reach a node with a particular index by starting at the
head node and moving link by link until the specified node is
reached. For long DLists
,
this would be very slow. A more efficient way to achieve the same
result is to use find()
to obtain an iterator positioned at the first occurrence and then
use the iterator-based version of remove()
to delete the node that the iterator is positioned at. Because
the iterator refers directly to the node, it is very fast to unlink
the node and no list traversal is necessary. The following example
illustrates this technique.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
public class Iterators5
{
public static void main( String[] args )
{
DList list = new DList();
list.add( "ape" );
list.add( "bat" );
list.add( "cat" );
list.add( "dog" );
System.out.println( "list = " + list );
DListIterator iterator = list.find( "cat" );
System.out.println( "iterator positioned @ " + iterator.get() );
list.remove( iterator );
System.out.println( "list = " + list );
}
}
Output
list = DList( ape, bat, cat, dog )
iterator positioned @ cat
list = DList( ape, bat, dog )
Another example of an iterator-oriented method is
equalRange()
in HashMap
.
This method returns a pair of iterators, with the first iterator
positioned at the first occurrence of a particular key and the
second iterator positioned immediately after the last occurrence
of the key.
Note that HashMapIterator
and OrderedMapIterator
both contain the methods key()
and value()
which allow you to read the key and value associated with the
iterators current position, respectively. They also both contain
a version of value()
that allows you to modify the value that the iterator is currently
positioned at.
The following example illustrates most of these features.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
public class Iterators6
{
public static void main( String[] args )
{
HashMap map = new HashMap( true ); // allow duplicates
map.add( "cat", "beauty" );
map.add( "dog", "marble" );
map.add( "cat", "agatha" );
map.add( "fox", "paula" );
System.out.println( "map = " + map );
Range range = map.equalRange( "cat" );
HashMapIterator iterator = (HashMapIterator) range.begin;
HashMapIterator last = (HashMapIterator) range.end;
while ( !iterator.equals( last ) )
{
System.out.print( "pair = " + iterator.get() );
System.out.print( ", key = " + iterator.key() );
System.out.println( ", value = " + iterator.value() );
iterator.advance();
}
}
}
Output
map = HashMap( Pair( dog, marble ), Pair( fox, paula ), Pair( cat, beauty ), Pair( cat, agatha ) )
pair = Pair( cat, beauty ), key = cat, value = beauty
pair = Pair( cat, agatha ), key = cat, value = agatha
Reverse iterators are just like regular iterators
except that they traverse a sequence backwards instead of forwards.
To iterator backwards through a container, construct a ReverseIterator
onto a BidirectionalIterator
that is positioned immediately after the end of the container.
nextElement()
moves a reverse iterator's underlying iterator one place backwards
and then returns its current item.
hasMoreElements()
returns true
until the reverse iterator has read the first element of the container.
The following example uses a reverse iterator for
iterating backwards through an Array
of Strings
.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.util.*;
public class Iterators7
{
public static void main( String[] args )
{
Array array = new Array();
array.add( "ape" );
array.add( "bat" );
array.add( "cat" );
array.add( "dog" );
System.out.println( "array = " + array );
ReverseIterator iterator = new ReverseIterator( array.end() );
while ( iterator.hasMoreElements() )
System.out.println( iterator.nextElement() );
}
}
Output
array = Array( ape, bat, cat, dog )
dog
cat
bat
ape
JGL includes an output iterator called an InsertIterator
that adds an object to a specified container using add()
whenever the iterator is written to. The following example uses
an InsertIterator
to append the results of a transform()
algorithm to a Deque
.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.algorithms.*;
import com.objectspace.jgl.functions.*;
import com.objectspace.jgl.util.*;
public class Iterators8
{
public static void main( String[] args )
{
Array array = new Array();
array.add( "ape" );
array.add( "giraffe" );
array.add( "elephant" );
System.out.println( "array = " + array );
Deque deque = new Deque();
InsertIterator iterator = new InsertIterator( deque );
UnaryFunction function = new LengthString();
Transforming.transform( array, iterator, function );
System.out.println( "deque = " + deque );
}
}
Output
array = Array( ape, giraffe, elephant )
deque = Deque( 3, 7, 8 )
JGL allows you to construct an iterator onto a java.io.OutputStream
by using the OutputStreamIterator
class. When an object is written using put(),
it is converted into a string using toString()
and then written to the output stream. The default constructor
connects the iterator to System.out
.
The following example uses an OutputStreamIterator
to send the contents of a Array
to the standard output stream.
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.algorithms.*;
import com.objectspace.jgl.util.*;
import java.util.Enumeration;
public class Iterators9
{
public static void main( String[] args )
{
Array array = new Array();
array.add( "ape" );
array.add( "bat" );
array.add( "cat" );
OutputStreamIterator output = new OutputStreamIterator( System.out );
Enumeration input = array.elements();
while ( input.hasMoreElements() )
output.put( input.nextElement() );
System.out.println(); // Flush output.
// You can use an algorithm to do the same thing.
Copying.copy( array, output );
System.out.println(); // Flush output.
}
}
Output
ape bat cat
ape bat cat