Iterables

Iterables

The reason that the for (E obj : collection) works is that all of the Java collections implement the Iterable interface (it is even a super-interface of the List interface that we have been working with). The Iterable interface has the method

Iterator<E> iterator();

that returns an Iterator object that allows us to loop through the items in a collection without having to use indices. We can create our own classes that implement Iterable and work with for-each loops.

Let’s create a class that holds a list of professors’ names. We’ll implement Iterable and have the iterator() return a class that implements Iterator<String>.

Open Professors.java in the Lab4 repo.

Add a private instance variable String[] names to the class and create a constructor that takes in an array of names, and makes a copy of it. Something like

Professors(String[] names) {
    this.names = names.clone();
}

(We made a deep copy using clone() so that if the names array were subsequently changed, it wouldn’t change the names of the professors!)

Next, add some code to main to create a new Professors object containing some professors’ names. Something like this.

String[] names = new String[] {
    "Eck", 
    "Feldman", 
    "Levinson", 
    "Taylor"
};

Professors profs = new Professors(names);

At this point, we can’t do anything interesting with profs.

Let’s make Professors implement Iterable. To do this, we need to change the public class Professors line to indicate it implements Iterable<String>.

public class Professors implements Iterable<String>

Because Professor implements Iterable, we need to add an iterator() method that returns something that implements Iterator<String>, but what? A new class! We will need to create a ProfessorIterator private inner class. At the top (or bottom, your choice) of the Professors class, add

private class ProfessorIterator implements Iterator<String> {
}

Notice that Professors implements Iterable<String> but ProfessorIterator implements Iterator<String>.

Now, we just need to write a constructor and implement hasNext() and next(), the two methods every iterator needs. A ProfessorIterator needs to know two pieces of information:

  • What list of professors is it iterating over?
  • How far into that list has it already iterated?

To keep track of that information, we need to add two instance variables to the ProfessorIterator. First, it’ll need a reference to the Professors object and second, it’ll need to keep an index into the list of names. Add the variables

private Professors profs;
private int index;

Write the constructor public ProfessorIterator(Professors profs) which just assigns (without cloning) profs to this.profs and sets index to 0.

Next, we will write the method public boolean hasNext(). This method will return true if we have not yet returned everything in the array of Professors’ names. We can do this by checking if this.index is less than this.profs.names.length.

Finally, we will write next(), the second method every iterator is required to have. Write public String next() to return the next professor’s name with “Professor " prepended to it. E.g,

String prof = "Professor " + this.profs.names[index];

Remember to update index to point to the next name before you return the current name!

Now, we will need to add an iterator() method to the Professor class that returns a new Iterator<String>. Add the following code to the Professor class

public Iterator<String> iterator() {
        return new ProfessorIterator(this);
    }

Note that this creates a new instance of the private ProfessorIterator class you created, passes it the current instance of the Professor class, and returns it.

If you’ve done everything correctly, you should be able to iterate over the professors. Add this code to the main method.

for (String prof : profs) {
    System.out.println(prof);
}

When you run your code, it should print

Professor Eck
Professor Feldman
Professor Levinson
Professor Taylor

An alternative way of accomplishing the same thing that demonstrates in more detail exactly how this for-each loop works is:

ProfessorIterator iterator = profs.iterator();
while(iterator.hasNext()) {
	String prof = iterator.next();
	System.out.println(prof);
}```