Пользовательский написанный итератор для набора выдает исключение в цикле for-each

Я пытаюсь написать собственный итератор для набора, который я сделал. Я немного запутался в контракте на интерфейс Iterable. Он имеет три метода: next(), hasNext() и remove(). Мой набор неизменяем, поэтому я планирую создать исключение UnsupportedOperationException для метода remove(). Это еще так называемая "ленивая генерация", т.е. элементы не хранятся в памяти, а создаются по мере необходимости, но это ни тут, ни там.

Javadoc для метода next() Iterator выглядит следующим образом:

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

а для hasNext() это:

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.)

Следуя этим правилам, я начал реализовывать свой Set и Iterator, получив следующее:

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;
    }
}

На мой взгляд, это нормально, next() возвращает следующее значение и выдает исключение, когда его больше нет. hasNext() должен возвращать true, если есть еще значения для перебора, и false в противном случае. Однако вывод основного цикла таков:

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)

Таким образом, кажется, что исключение не обрабатывается в цикле for each. Как мне написать собственный итератор, который я могу использовать, чтобы это работало? Я попытался вернуть null вместо того, чтобы генерировать исключение, но это просто дает исключение NullPointerException.

Должен ли я возвращать ноль, когда итератор завершен, или генерировать исключение? Javadoc говорит, что next() должен генерировать исключение, но когда я наводил курсор на переопределенный метод next() в Eclipse, подпись не показывает генерацию NoSuchElementException, поэтому я очень запутался в том, что говорит контракт. Мне кажется очень странным возвращать null, когда я закончу, поскольку коллекция потенциально может содержать нулевые элементы.

Спасибо за помощь.


person user1661303    schedule 18.08.2017    source источник
comment
Почему вы перебираете hasNext() в своей реализации next()? Вы должны только проверить, есть ли следующий элемент без цикла while.   -  person dpr    schedule 18.08.2017
comment
Я думаю, что сделал это, потому что логика заключается в том, чтобы проверить, может ли итератор вернуть больше элементов, чтобы избежать дублирования кода и написания if(current ‹ max) два разных раза. current выполняет итерацию по всем натуральным числам, поэтому необходимо проверить, что current является простым числом и что оно меньше максимального значения. Поскольку не все натуральные числа являются простыми, мне нужно продолжать увеличивать ток, пока я не достигну простого числа или предела, поэтому цикл while.   -  person user1661303    schedule 18.08.2017
comment
@user1661303 user1661303 логика не имеет смысла, приятель, next() должен возвращать одно значение (не перебирая его в цикле). и этот код всегда будет выдавать исключение, потому что после перебора всех элементов вы в конечном итоге выдаете исключение независимо   -  person nafas    schedule 18.08.2017


Ответы (2)


Изменять

while(hasNext()) {
    //...
}
throw new NoSuchElementException();

To

if(hasNext()) {
    //...
} else {
    throw new NoSuchElementException();
}
person Valentun    schedule 18.08.2017
comment
Ах. В порядке. Я почти понимаю, о чем вы говорите, но я не понимаю, как цикл while в моем примере перебирает элементы в наборе. Разве он не просто перебирает натуральные числа, пока не наткнется на элемент, который находится в наборе, или не достигнет предела, а затем выдаст исключение? Скажем, например, текущий равен 3. Затем вызов next() должен просто увеличить его до 4, затем увеличить до 5, затем увидеть, что это простое число, и вернуть его правильно? Не перебирать весь набор. Может быть, я что-то упускаю здесь. - person user1661303; 18.08.2017
comment
Это была моя первая мысль, но это не решит проблему. Цикл необходим для вычисления следующего простого числа. - person dpr; 18.08.2017
comment
@dpr, но цикл должен быть вне итератора, не так ли? - person Valentun; 18.08.2017
comment
Этот код кажется правильным, но возвращает [2, 3, 4, 5, 6, 7, 8, 9, 10] как набор, когда я его запускаю. -редактировать - person user1661303; 18.08.2017
comment
@user1661303 user1661303, простите, это не итерация, она увеличивает текущую переменную и вызывает исключение - person Valentun; 18.08.2017
comment
@ user1661303 Какой смысл принимать ответ, который явно не решает вашу проблему ?? - person dpr; 18.08.2017
comment
@ dpr26 Я подправил его ответ, и вместе с вашим объяснением мне удалось найти решение, которое, похоже, сработало. Люди обычно злятся, если они вносят свой вклад, а ответ не помечается как принятый, вот почему. - person user1661303; 18.08.2017
comment
@user1661303 user1661303 вы можете не принять мой ответ, если он слишком важен для dpr. Я просто люблю помогать людям) - person Valentun; 18.08.2017

Предположим, что последним простым числом, возвращаемым вашим кодом, является 7. Тогда current будет 7, что, очевидно, равно < 10. Итак, hasNext() вернет true. Однако нет больше простого числа больше 7, но меньше 10, поэтому следующий вызов next() даст NoSuchElementException. В настоящее время ваш код будет работать только в том случае, если max является простым числом.

Вам нужно будет убедиться, что в hasNext() есть дополнительное простое число.

person dpr    schedule 18.08.2017
comment
Ах. Понятно. Спасибо за объяснение углового случая. Теперь я понимаю :) - person user1661303; 18.08.2017