I'm trying to write a custom made Iterator for a Set that I made. I'm a bit confused about the contract for the Interface Iterable. It has three methods: next(), hasNext() and remove(). My set is immutable so I'm planning on throwing an UnsupportedOperationException for the method remove(). It is also so called "lazily generated" ie elements are not stored in a memory but rather created when needed, but that's neither here nor there.
The javadoc for the Iterator's next() method is as follows:
E next()
Returns the next element in the iteration.
Returns:
the next element in the iteration
Throws:
NoSuchElementException - if the iteration has no more elements
and for hasNext() is:
boolean hasNext()
Returns true if the iteration has more elements. (In other words, returns
true if next() would return an element rather than throwing an exception.)
Going by these rules, I started implementing my Set and Iterator, getting this:
import java.util.AbstractSet;
import java.util.Iterator;
public class PrimesBelow extends AbstractSet<Integer>{
int max;
int size;
public PrimesBelow(int max) {
this.max = max;
}
@Override
public Iterator<Integer> iterator() {
return new SetIterator<Integer>(this);
}
@Override
public int size() {
if(this.size == -1){
System.out.println("Calculating size");
size = calculateSize();
}else{
System.out.println("Accessing calculated size");
}
return size;
}
private int calculateSize() {
int c = 0;
for(Integer p: this)
c++;
return c;
}
public static void main(String[] args){
PrimesBelow primesBelow10 = new PrimesBelow(10);
for(int i: primesBelow10)
System.out.println(i);
System.out.println(primesBelow10);
}
}
.
import java.util.Iterator;
import java.util.NoSuchElementException;
public class SetIterator<T> implements Iterator<Integer> {
int max;
int current;
public SetIterator(PrimesBelow pb) {
this.max= pb.max;
current = 1;
}
@Override
public boolean hasNext() {
if(current < max) return true;
else return false;
}
@Override
public Integer next() {
while(hasNext()){
current++;
if(isPrime(current)){
System.out.println("returning "+current);
return current;
}
}
throw new NoSuchElementException();
}
private boolean isPrime(int a) {
if(a<2) return false;
for(int i = 2; i < a; i++) if((a%i)==0) return false;
return true;
}
}
This seemed fine by my, next() return the next value and throws an Exception when there is no more. hasNext() should return true if there are more values to iterate over and false otherwise. The output from the main loop however is this:
returning 2
2
returning 3
3
returning 5
5
returning 7
7
Exception in thread "main" java.util.NoSuchElementException
at SetIterator.next(SetIterator.java:27)
at SetIterator.next(SetIterator.java:1)
at PrimesBelow.main(PrimesBelow.java:38)
So it seems the Exception isn't handled in the for each loop. How do I write a custom Iterator that I can use so that this works? I tried returning null instead of throwing an Exception but that just gives a NullPointerException.
Should I return null when the Iterator is finished, or throw an Exception? Javadoc says next() should throw an Exception but when I hover over the overriden method next() in Eclipse the signature doesn't show throws NoSuchElementException so I'm very confused as to what the contract says. It seems very weird to me to return null when I'm done since the collection could potentially contain null elements.
Thank for the help.
Change
while(hasNext()) {
//...
}
throw new NoSuchElementException();
To
if(hasNext()) {
//...
} else {
throw new NoSuchElementException();
}