10. Iterators, Input Parsing

Part of CS:2820 Object Oriented Software Development Notes, Sprig 2017
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

 

Iterators

In the last class, we used Java's for loop construct:

        /** Print out the road network from the data structure
         */
        static void printNetwork() {
                for (Intersection i:inters) {
                        System.out.println( i.toString() );
                }
                for (Road r:roads) {
                        System.out.println( r.toString() );
                }
        }

Each of these for loops is actually an abbreviation for a rather complex bit of code. First, the for loop creates an iterator over the list and then it use that iterator to get successive list elements. The following far more long-winded bit of code is exactly equivalent to the first loop above:

                for (
                        Iterator <Intersection> it = inters.iterator();
                        it.hasNext();
                ) {
                        Intersection i = it.next();
                        System.out.println( i.toString() );
                }

An iterator is class that can be derived from any collection that will deliver successive members of that collection each time its next() method is called.

The above long-winded code expresses exactly the same computation as the original for loop, and in fact, the Java compiler generates exactly the same code from the original and this long-winded version of the code. Any for loop can be rewritten as a while loop, so we could further deconstruct this code as follows:

                {
                        Iterator <Intersection> it = inters.iterator();
                        while ( it.hasNext() ) {
                                Intersection i = it.next();
                        	System.out.println( i.toString() );
                        }
                }

Why did we wrap the whole lump of code above in braces? The iterator it created by the for loop was local to the loop. When we converted the for loop to a while loop, we added the extra brackets to guarantee that the declaration of it would be local to the loop and not visible elsewhere in the program.

As a general rule, the for-loop construct in Java is always a shorthand. Every Java for loop can be rewritten as a while loop. Consider this elementary for loop that iterates over integers:

for(int i=0 ; i < 10 ; i++)
{
        doSomethingWith( i );
}

This can be rewritten as follows, and in fact, the Java compiler would generate exactly the same code from the above as it generates from this long-winded rewrite:

{
        int i = 0;
        while (i < 10) {
                doSomethingWith( i );
        }
}

Again, we wrapped the while loop in an extra set of braces in order to make the loop control variable i visible only inside the loop and not elsewhere in the program.

Where were we?

If we try to compile the code we had at the end of the previous class, we get these error messages:

[dwjones@serv15 ~/project]$ javac Road*java
RoadNetwork.java:47: error: cannot find symbol
			sourceName + " " +
			^
  symbol:   variable sourceName
  location: class Road
RoadNetwork.java:48: error: cannot find symbol
			dstName + " " +
			^
  symbol:   variable dstName
  location: class Road
2 errors
[dwjones@serv15 ~/project]$

Looking at the relevant code, we find:

class Road {
        float travelTime;               // measured in seconds
        Intersection destination;       // where road goes
        Intersection source;            // where road comes from
        // name of road is source-destination

        // initializer
        public Road( Scanner sc, LinkedList  inters ) {
                // scan and process one road
                String sourceName = sc.next();
                String dstName = sc.next();
                // Bug: look up source and dest names!
                travelTime = sc.nextFloat();
                // Bug:  What if no next float?
                String skip = sc.nextLine();
                // Bug:  What if more junk at end of line (skip not empty)
        }

        // other methods
        public String toString() {
                return (
                        "Road " +
                        sourceName + " " +
                        dstName + " " +
                        travelTime
                );
        }
}

The toString() method is trying to access local variables of the Road() initializer when it ought to be relying on the information available from the instance variables of class Road().

Visibility

We could replace sourceName in the toString() method above with source.name. This assumes that we want the name() field of the object to be visible from outside, and this raises the question, what fields ought to be visible, and if visible, how can we protect them.

There is a rule of thumb in programming that comes from military security: The need to know rule. This states that no user of an object should be given more information about the internal state of that object than they need to know to get their job done.

Need to know security goes against the principle of transparency, that in an open society, secrets are bad. The point of keeping secrets in object-oriented programming is to minimize the size of the public interface of each class.

In Java, each declaration can be marked as: public, private, or final. In general, marking declarations as private makes very good sense. It means that the variable is invisible to all code outside this class. If a variable must be visible because outsiders need to know about it, you can minimize the damage they can do by marking it final. Final variables are not read-only, their values can be changed, but the binding of the variable name to the object is fixed for all time. Declare variables to be public only if outsiders have a natural need to not only see the variable but to make arbitrary changes to it.

In our running example road network code, the following declarations make sense:

class Road {
        private float travelTime;        // measured in seconds
        private Intersection destination;// where road goes
        private Intersection source;     // where road comes from

        ...
}

class Intersection {
        final String name;
        private LinkedList  outgoing = new LinkedList  ();
        private LinkedList  incoming = new LinkedList  ();

        ...

The markings above make all fields private except for the name of an intersection, which is final. That declaration may work, unless the structure of the initializer(s) is complex enough that the Java compiler cannot tell that each initializer has exactly one and only one assignment this field.

Lookup

The next problem we face is that of looking up each intersection. The need for this is announced by a bug notice in our code:

class Road {

        ...

        // initializer
        public Road( Scanner sc, LinkedList  inters ) {
                // scan and process one road
                String sourceName = sc.next();
                String dstName = sc.next();
                // Bug: look up source and dest names!

                ...
        }
        ...
}

class Intersection {

        ...

        // initializer
        public Intersection( Scanner sc, LinkedList  inters ) {
                // scan and process one intersection
                name = sc.next();
                // Bug: look up name!
          
                ...
        }
        ...
}

Obviously, we need a way, given the name of an intersection, to find that intersection in the list of all intersections. Since this list is a static field of class RoadNetwork, one way to do the lookup is to have RoadNetwork export a public static method to look things up.

public class RoadNetwork {
        static LinkedList  inters
                = new LinkedList  ();

        ...

        static Intersection findIntersection( String s ) {
                // return the intersection named s, or null if no such
		// Bug: flesh this out!
        }

        ...
}

The obvious way to code this is with a rather stupid linear search through the list of roads to find the right one. Java includes some far more sophisticated tools for this search, but we'll ignore them here.