Iterators

In any container class, you must have a way to put things in and a way to get things out. After all, thats the primary job of a containerto hold things. In the ArrayList, add( ) is the way that you insert objects, and get( ) is one way to get things out. ArrayList is quite flexible; you can select anything at any time, and select multiple elements at once using different indexes.

If you want to start thinking at a higher level, theres a drawback: You need to know the exact type of the container in order to use it. This might not seem bad at first, but what if you start out using an ArrayList, and later on you discover that because of the features you need in the container you actually need to use a Set instead? Or suppose youd like to write a piece of generic code that doesnt know or care what type of container its working with, so that it could be used on different types of containers without rewriting that code?

The concept of an iterator (yet another design pattern) can be used to achieve this abstraction. An iterator is an object whose job is to move through a sequence of objects and select each object in that sequence without the client programmer knowing or caring about the underlying structure of that sequence. In addition, an iterator is usually whats called a light-weight object: one thats cheap to create. For that reason, youll often find seemingly strange constraints for iterators; for example, some iterators can move in only one direction.

The Java Iterator is an example of an iterator with these kinds of constraints. Theres not much you can do with one except:

Ask a container to hand you an Iterator using a method called
iterator( ).This Iterator will be ready to return the
first element in the sequence on your first call to its next( )
method.

Get the next object in the sequence with next( ).

See if there are any more objects in the sequence with
hasNext( ).

Remove the last element returned by the iterator with
remove( ).

Thats all. Its a simple implementation of an iterator, but still powerful (and theres a more sophisticated ListIterator for Lists). To see how it works, lets revisit the CatsAndDogs.java program from earlier in this chapter. In the original version, the method get( ) was used to select each element, but in the following modified version, an Iterator is used:

You can see that the last few lines now use an Iterator to step through the sequence instead of a for loop. With the Iterator, you dont need to worry about the number of elements in the container. Thats taken care of for you by hasNext( ) and next( ).

As another example, consider the creation of a general-purpose printing method:

Look closely at printAll( ). Note that theres no information about the type of sequence. All you have is an Iterator, and thats all you need to know about the sequence: that you can get the next object, and that you can know when youre at the end. This idea of taking a container of objects and passing through it to perform an operation on each one is powerful and will be seen throughout this book.

The example is even more generic, since it implicitly uses the Object.toString( ) method. The println( ) method is overloaded for all the primitive types as well as Object; in each case, a String is automatically produced by calling the appropriate toString( ) method.

Although its unnecessary, you can be more explicit using a cast, which has the effect of calling toString( ):

System.out.println((String)e.next());

In general, however, youll want to do something more than call Object methods, so youll run up against the type-casting issue again. You must assume youve gotten an Iterator to a sequence of the particular type youre interested in, and cast the resulting objects to that type (getting a run-time exception if youre wrong).

You could write printAll( ) to accept a Collection object instead of an Iterator, but the latter provides better decoupling.

Unintended recursion

Because (like every other class) the Java standard containers are inherited from Object, theycontain a toString( ) method. This has been overridden so that they can produce a String representation of themselves, including the objects they hold. Inside ArrayList, for example, the toString( ) steps through the elements of the ArrayList and calls toString( ) for each one. Suppose youd like to print the address of your class. It seems to make sense to simply refer to this (in particular, C++ programmers are prone to this approach):

If you simply create an InfiniteRecursion object and then print it, youll get an endless sequence of exceptions. This is also true if you place the InfiniteRecursion objects in an ArrayList and print that ArrayList as shown here. Whats happening is automatic type conversion for Strings. When you say:

"InfiniteRecursion address: " + this

The compiler sees a String followed by a + andsomething thats not a String, so it tries to convert this to a String. It does this conversion by calling toString( ), which produces a recursive call.

If you really do want to print the address of the object in this case, the solution is to call the ObjecttoString( ) method, which does just that. So instead of saying this, youd say super.toString( ).