← Data abstraction CS Skill Tree

InterfaceIterator

Extra practice. The lesson on the interface the official list does name is Implementing Comparable.

The official topic list names one interface and no other: Implementing an interface, such as Comparable. An iterator is not on that list. Work it once you are sure of Comparable, because writing one is the same skill on a harder promise, and because the practice exam includes one.

You write 2 short methods inside a nested class. Everything else in the file is already written. You check them by running the provided Tester and holding what your program prints against the expected output, line for line.

Your readiness not started

Your own marking on this problem.

What the two methods must do

MyList keeps its values in an ArrayList<String>. It is declared implements Iterable, which is a promise that you can walk it with the enhanced for loop. That promise is only kept if something can hand out a cursor and step it one value at a time. The cursor is the nested class MyListIterator. It is written for you except for the two methods that make it move.

Both signatures are already in the file. Do not change them.

public boolean hasNext()

public Object next()

next returns Object, not String. The class is written against the raw Iterator type, so Object is the declared return. Leave it that way. Your compiler may print a warning about raw types. A warning is not an error and it costs you nothing.

What is already written for you

Rules that come with this problem

Follow these, and read the instructions you are handed on the day, because that is the only thing that says what breaks a problem.

  • No helper methods. Each body is complete in itself. You do not get to add a private advance.
  • Do not edit the driver. Here the driver is Tester. Your two methods have to make its output correct without it changing at all.
  • Do not change any variable. cur and listRef keep their names, their types and their modifiers, and you do not add a field to the class.
  • No new imports. The file you are given already has import java.util.*; at the top. Leave that line where it is and add nothing beside it.

And the one the official site puts plainly, on its Basic Strategies page: You may receive points for compiling code but you WON’T receive points for code that does not compile. When you are running out of time, put the half-written method back to the stub you were given and make the project build.

The shape of the answer

hasNext is one comparison

The values sit at positions 0 through listRef.size() - 1. cur holds the position of the value that has not been handed out yet. So there is still something left exactly when cur is inside the list, and that is a comparison between cur and listRef.size(), strictly less than. Return the comparison. You can wrap it in an if that returns true or false, and it is not wrong, but it is more to get right under time pressure.

Two things hasNext must not do. It must not move cur, or the loop skips every other value. It must not ask listRef for a value in order to find out whether one is there, because asking for a position that does not exist is what hasNext exists to prevent.

next is three moves, in this order

  1. Read. Get the value at position cur out of listRef and hold it in a local variable.
  2. Move. Add one to cur.
  3. Return. Give back the value you held, not a fresh read after the move.

The order is the whole method. Move first and you hand back the wrong value and run off the end on the last call. A local variable inside your own method body is fine. The rule about not changing variables covers the fields and the signatures you were given, not a temporary you declare inside your own method.

The check that catches most of it

Say the two sentences out loud before you write anything. hasNext looks and does not touch. next reads, then steps, then returns. If your code does not read like those two sentences, fix it before you run it.

Trace it once

The list holds apple, banana, cherry, so listRef.size() is 3 and a fresh cursor starts at cur 0.

Callcur beforeResultcur after
hasNext00 < 3, true0
next0apple1
next1banana2
next2cherry3
hasNext33 < 3, false3

Read two things off that table. cur ends at size, not at size - 1, and that is correct. And hasNext is the only thing stopping the walk, which is why its comparison carries the safety of the whole method.

The enhanced for loop is that same walk

This line:

for (Object o : list)
   System.out.println(o);

is compiled into this:

Iterator it = list.iterator();
while (it.hasNext())
   System.out.println(it.next());

Nothing else happens. So the symptoms map straight back to your code. A for-each loop that prints nothing means hasNext returned false the first time it was asked. A for-each loop that never stops means next is not moving cur. A for-each loop that misses values means something other than next is moving cur.

Notice that the loop starts by calling iterator(), and iterator() builds a new MyListIterator at position 0. That is why the same list can be walked twice.

The cases, one at a time

Tester puts your 2 methods through 5 situations. Every one of them shows up in the output block, so work all 5.

1. Three values, walked with an explicit iterator

Tester gets an iterator into a variable named iter and runs a plain while (iter.hasNext()) loop. This is the straight case. It prints apple, then banana, then cherry. If this one is wrong, all of them are.

2. The same three values, with the enhanced for loop

The explicit walk just left its cursor at the end. The for-each loop still prints all three again, because it calls iterator() and gets its own cursor at 0. Nothing you write resets anything. If you were tempted to make next wrap back around to 0 when it runs out, this is the case that shows why you must not.

3. A list with one value

hasNext is true once, next returns single item, then hasNext is false because cur is 1 and the size is 1. This is the case that separates < from <=. With <= the loop asks for a second value, listRef.get(1) throws IndexOutOfBoundsException, and the program stops there, so everything printed after this point goes missing too.

4. An empty list, with the enhanced for loop

The size is 0 and cur is 0, so hasNext has to be false on the very first ask and the loop body must never run. In the expected output there is nothing at all under that heading. This is where 0 < 0 being false does the work for you, with no special case and no if for the empty list.

5. remove on the used-up iterator

At the end, Tester calls iter.remove() on the iterator from situation 1 and catches the exception. That method is already written and it throws whatever state the cursor is in, so this case comes out right as long as you have not touched it. Leave it alone.

Two cases the driver does not run

Calling next when nothing is left. The Iterator contract in the Java API says throw NoSuchElementException. Tester never does this, so the printed output is the same either way. If you want to add the throw, NoSuchElementException is already available through the import that is in the file, so adding it breaks no rule. If you are short on time, skip it and get the 5 situations above right first.

Changing the list while a walk is running. Not tested here. Do not build for it.

The output you are being compared against

Run Tester and compare every line, blank lines included. Your editor may wrap the run in its own banner lines above and below; those are not part of the comparison.

Using an explicit iterator to walk through list
apple
banana
cherry

Using enhanced for loop to test Iterator
apple
banana
cherry

Testing on a single item list
single item

Testing on MyList object that is empty using enhanced for loop

Unsupported Operation Exception on call to remove.
remove not implemented for MyListIterator

Look at the gap after the empty list heading. That gap is the empty list printing nothing, followed by the blank line that opens the next message. If anything appears in that gap, your hasNext said true on an empty list.

Drill

Four questions. Work each one on paper before you tap.

Level-up check

1. Predict the output. A student writes this, then runs the empty list part of Tester.

public boolean hasNext()
{
   return this.cur <= this.listRef.size();
}

2. Spot the bug. The three value list holds apple banana cherry. What does this next do?

public Object next()
{
   this.cur++;
   return this.listRef.get(this.cur);
}

3. Spot the bug. hasNext is correct. What happens when the enhanced for loop runs against this next?

public Object next()
{
   return this.listRef.get(this.cur);
}

4. Why twice? Tester walks the three value list with an explicit iterator until hasNext is false. The very next thing it does is a for-each loop over that same list, and the expected output shows all three values a second time. Why?

No answers yet.

Write it out

These two are not scored. Type them from memory into the box, then open the check and hold your version against it. The box is scratch space and is not saved.

Write itThe body of hasNext

One line. You have cur and listRef and nothing else.

Write itThe body of next

Three lines. Say the order out loud before you type it.

Your checklist

Tick these when they are true.

Could you write this cold?

Not "did I follow along". Blank file, no notes, 4 hours on a clock, and no way to look anything up. Say yes only when that is true. You can take it back at any time.

If a piece of this is shaky

Read more, from the people who wrote it

Everything here was fetched and is the page it says it is.