Cartesian Plane Lesson 4 Page 1: Iterators and Iterables

Iterators, Iterables

On this page, we will discuss the Iterator<E> and Iterator<E> interfaces.

GitHub repository: Cartesian Plane Part 4

Previous page: Cartesian Plane Lesson 4: Drawing the Tick Marks and Labels

Interfaces

Let’s have a quick review of interfaces. An interface is a type, much like a class. Interfaces contain descriptions of methods without the method implementations (traditionally, interfaces contain no implementation code at all; this is no longer true, but let’s leave that discussion for another time). A class can implement one or more interfaces; if a class implements an interface, it is required to implement the methods described by the interface (or declare itself abstract, which can defer method implementation to concrete subclasses). One of the most commonly implemented interfaces is Runnable. If you look at the documentation for Runnable, you will see that it describes the run method as follows:

    void run()

The Root class that we have been using in our lessons is an example of a class that implements Runnable; in summary, it looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Root implements Runnable
{
    // ...
    /**
     * Required by the Runnable interface.
     * This method is the place where the initial content
     * of the frame must be configured.
     */
    public void run()
    {
    	/* Instantiate the frame. */
        frame = new JFrame( "Graphics Frame" );
        // ...
    }
    // ...
}        

Interfaces can be parameterized. Parameterized interfaces can be tailored to work with a specific type. One of the most popular such interfaces is List<E>. List<E> is declared like this:

    interface List<E> { ... }

It describes methods declared this way:

    void add​(int index, E element);
    E get(int index)

When you declare a variable of type List, you substitute a type for the E; you’ve probably already done something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class ListDemo
{
    public static void main( String[] args )
    {
        List<String>    list    = new ArrayList<>();
        list.add( "string 1" );
        list.add( "string 2" );
        String          str     = list.get( 1 );
        System.out.println( str );
    }
}

In your code, the type you declare inside the diamond operator (<String>) is substituted for E, and now any operations on your list are limited to use with Strings.

Parameterization can be a complex topic, but for now, we’ve covered all we need to know. To learn more about interfaces, see the Oracle tutorial Interfaces and Inheritance. For more about parameterization, see the Oracle Tutorial Generics.

A bit more about Parameterization (also referred to as Generics):
You cannot parameterize on a primitive type, only on a class or an interface. You cannot have a List of ints or booleans. Recall, however, that every primitive type has a corresponding wrapper class, so you can have a List of type Integer or Boolean. This may be confusing if you’ve ever seen code that looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class AutoboxingDemo
{
    public static void main(String[] args)
    {
        List<Integer>   iList   = new ArrayList<>();
        for ( int inx = 0 ; inx < 10 ; ++inx )
            iList.add( inx );
        iList.set( 5, 42 );
    }
}

To clear up the confusion, the code at lines 7 and 8 does not add primitive values to iList; instead, the compiler automatically converts the int primitive to an instance of its wrapper class, Integer, in an operation called autoboxing. The above code is equivalent to this:

for ( int inx = 0 ; inx < 10 ; ++inx )
    iList.add( Integer.valueOf( inx ) );
iList.set( 5, Integer.valueOf( 42 ) );

Iterators and Iterables

Here, we will discuss iterators, which can produce a sequence of values, and iterables, which encapsulate a sequence of values and can provide an iterator to traverse the sequence.

Iterators

In Java, an iterator is something that produces a sequence of values. The generic interface Iterator<E> describes a type that produces a sequence of values of type E. Look at the documentation for Iterator. As you can see, to implement an Iterator<E>, you must write two methods:

    boolean hasNext()
    E next()

The method next() returns the next value in the sequence (assuming there is one). The hasNext() method returns true if the iterator has a next element. If hasNext() is false, calling next() precipitates a NoSuchElementException. A common example of an iterator is ListIterator<E>. If you have a variable of type List<String>, invoking the method listIterator() returns an object of type ListIterator<String> that can be used to traverse the contents of the list. Here’s an example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class listIteratorDemo
{
    public static void main(String[] args)
    {
        List<String>    list    = new ArrayList<>();
        list.add( "every" );
        list.add( "good" );
        list.add( "boy" );
        list.add( "deserves" );
        list.add( "favor" );
        
        ListIterator<String>    iter    = list.listIterator();
        while ( iter.hasNext() )
        {
            String  str = iter.next();
            System.out.println( str );
        }
    }
}

Writing your own iterator isn’t difficult. You need an object that stores enough state to know the first and next elements to produce and when the sequence has been exhausted. Let’s write the class IntIterator, which implements Iterator<Integer> and iterates over a range of integers. We’ll have a constructor that describes the target range, the instance variables for the next integer to produce, and the range’s upper bound.

Note: Our IntIterator class encapsulates all integers in the range [lowerBound, upperBound). Recall from math class that this notation means lowerBound is a part of the range, but upperBound is not.

private final int   upperBound;
private int         next;

public IntIterator( int lowerBound, int upperBound )
{
    this.upperBound = upperBound;
    next = lowerBound;
}

Now we need a hasNext() method that compares next to upperBound, and a next() method that correctly manipulates the value of next, throwing an exception if next doesn’t exist. Here’s the complete class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class IntIterator implements Iterator<Integer>
{
    private final int   upperBound;
    private int         next;
    
    // Iterates over the sequence num, 
    //where lowerBound <= num < upperBound
    public IntIterator( int lowerBound, int upperBound )
    {
        this.upperBound = upperBound;
        next = lowerBound;
    }
    @Override
    public boolean hasNext()
    {
        boolean hasNext = next < upperBound;
        return hasNext;
    }
    /**
     * Returns the next element in the range.
     * 
     * Bloch Item 74 
     * "Use the Javadoc @throws tag to document each exception
     * that a method can throw, but do not use the throws keyword 
     * on unchecked exceptions."
     * (Bloch, Joshua. Effective Java (p. 304). Pearson Education. Kindle Edition.) 
     *
     * @throws  NoSuchElementException if the bounds
     *          of the iterator are exceeded
     */
    @Override
    public Integer next()
    {
        if ( next >= upperBound )
            throw new NoSuchElementException( "iterator exhausted" );
        int nextInt = next++;
        return nextInt;
    }
}

Iterables

A class that implements Iterable<E> simply has a method named iterator() that returns an object of type Iterator<E>. The List<E> interface has such a method, allowing us to say that a List<E> is iterable. The nice thing about iterable objects is that they can be used in an enhanced for statement (a.k.a. for-each loop). Here’s an example:

Note: For more about the enhanced for statement, see The for Statement in the Oracle Java tutorial.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class ForEachDemo
{
    public static void main( String[] args )
    {
        List<String>    list    = new ArrayList<>();
        list.add( "every" );
        list.add( "good" );
        list.add( "boy" );
        list.add( "deserves" );
        list.add( "favor" );
        
        for ( String str : list )
            System.out.println( str );
    }
}

If we want a class that produces objects that can iterate over a range of integers inum1 through inumN, all we need to do is implement the method Iterator<Integer> iterator() that returns new IntIterator(inum1, inumN). Let’s write such a class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class Range implements Iterable<Integer>
{
    private final int   lowerBound;
    private final int   upperBound;
    
    // Iterable over num,
    // where lowerBound <= num < upperBound
    public Range( int lowerBound, int upperBound )
    {
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
    }
    
    public Iterator<Integer> iterator()
    {
        IntIterator iter    = new IntIterator( lowerBound, upperBound );
        return iter;
    }
}

And here’s an example that uses our Range class.

1
2
3
4
5
6
7
8
9
public class RangeDemo
{
    public static void main(String[] args)
    {
        Range   range   = new Range( -10, 10 );
        for ( int num : range )
            System.out.println( num );
    }
}

A Quick Look at Inner Classes

Another strategy for creating iterable classes is to declare the iterator class directly inside the class that uses it. A class declared directly inside another class is called a nested class. The class may be declared static; if it is not static, it is an inner class. The advantage of inner classes is that they have access to all the instance variables and instance methods inside the class that contains them (called the outer class). Here’s a trivial example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class InnerClassDemo
{
    private double      datum;
    private DatumRoot   fourthRoot;
    
    public static void main( String[] args )
    {
        InnerClassDemo  demo    = new InnerClassDemo( 16 );
        System.out.println( demo.fourthRoot.getRoot() );
    }
    
    public InnerClassDemo( double datum )
    {
        this.datum = datum;
        fourthRoot = new DatumRoot( 4 );
    }
    
    private void printMessage( String msg )
    {
        System.out.println( "Message: " + msg );
    }
    
    private class DatumRoot
    {
        private double  radicand;
        
        private DatumRoot( double radicand )
        {
            this.radicand = radicand;
        }
        
        private double getRoot()
        {
            printMessage( "calculating root" );
            double  root    = Math.pow( datum, 1 / radicand );
            return root;
        }
    }
}

The above code serves no purpose other than to demonstrate that an instance of the inner class (DatumRoot) can access the instance variable (datum) and instance method (printMessage) of the outer class (InnerClassDemo).

We can apply this strategy to the Range class, for example. If we declare the iterator to be an inner class in Range, we have a) a Range class with no external dependencies on other classes and b) a simplified iterator that doesn’t have to store lowerBound and upperBound in its own state; instead, it obtains these values from the outer class. Here is the code for the enhanced Range class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class RangeWithInnerClass implements Iterable<Integer>
{
    private final int   lowerBound;
    private final int   upperBound;
    
    // Iterable over num,
    // where lowerBound <= num < upperBound
    public RangeWithInnerClass( int lowerBound, int upperBound )
    {
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
    }
    
    public Iterator<Integer> iterator()
    {
        InnerIntIterator    iter    = new InnerIntIterator();
        return iter;
    }
    
    private class InnerIntIterator implements Iterator<Integer>
    {
        private int         next    = lowerBound;
        
        @Override
        public boolean hasNext()
        {
            boolean hasNext = next < upperBound;
            return hasNext;
        }
        @Override
        public Integer next()
        {
            if ( next >= upperBound )
                throw new NoSuchElementException( "iterator exhausted" );
            int nextInt = next++;
            return nextInt;
        }
        
    }
}

See Nested Classes in the Oracle Java Tutorial for more information about inner classes.

Summary

On this page, we discussed the Iterator<E> and Iterable<E> interfaces, which make it easy to traverse a set of values sequentially. On the next page, we will use these interfaces to develop the LineGenerator class, which will calculate the line coordinates for drawing gridlines, axes, tick marks, and labels.

Next: Cartesian Plane Lesson 4 Page 2: The LineGenerator Class