Solution: Knowing When to Stop
There is no array or linked list to traverse here, so the iterator’s state is the current value and whether we have finished.
public class MathSequence implements Iterable<Integer> {
private final int start;
public MathSequence(int n) {
if (n <= 0) {
throw new IllegalArgumentException("start must be positive");
}
this.start = n;
}
@Override
public Iterator<Integer> iterator() {
return new SequenceIterator();
}
private class SequenceIterator implements Iterator<Integer> {
private int current = start;
private boolean finished = false;
@Override
public boolean hasNext() {
return !finished;
}
@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
int value = current;
if (current == 1) {
finished = true; // we just handed out the final 1
} else if (current % 2 == 0) {
current = current / 2;
} else {
current = current * 3 + 1;
}
return value;
}
}
}
Each next() saves the current value to return, then prepares the next one with a single arithmetic step: divide by 2, or triple and add one. That is constant time, and the only state kept is the two fields, so it is constant space too.
You might want to write hasNext() as current != 1, but that is wrong. It would return false while 1 is still waiting to be returned, so the sequence would stop one value early and never return its final 1. The finished flag fixes that. It starts false, and it becomes true only when next() returns the 1, so the next hasNext() returns false. The edge case n = 1 needs no special handling: the first next() returns 1 and sets finished, and the sequence is the single value 1.