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:
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);
}```