Можем ли мы написать собственный итератор в Java?

Если у меня есть список, содержащий [alice, bob, abigail, charlie], и я хочу написать итератор, чтобы он повторял элементы, начинающиеся с "a", могу ли я написать свой собственный? Как я могу это сделать?

Ответ 1

Конечно. Итератор - это просто реализация интерфейса java.util.Iterator. Если вы используете существующий итерируемый объект (например, LinkedList) из java.util, вам нужно либо подклассировать его, либо переопределить его функцию iterator, чтобы вы вернули свой собственный или предоставили средство для обертывания стандартный итератор в вашем специальном экземпляре iterator (который имеет то преимущество, что его более широко используют) и т.д.

Ответ 2

Лучшим вариантом многократного использования является реализация интерфейса Iterable и переопределение метода iterator().

Вот пример класса ArrayList, реализующего интерфейс, в котором вы переопределяете метод Iterator().

import java.util.Iterator;

public class SOList<Type> implements Iterable<Type> {

    private Type[] arrayList;
    private int currentSize;

    public SOList(Type[] newArray) {
        this.arrayList = newArray;
        this.currentSize = arrayList.length;
    }

    @Override
    public Iterator<Type> iterator() {
        Iterator<Type> it = new Iterator<Type>() {

            private int currentIndex = 0;

            @Override
            public boolean hasNext() {
                return currentIndex < currentSize && arrayList[currentIndex] != null;
            }

            @Override
            public Type next() {
                return arrayList[currentIndex++];
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
        return it;
    }
}

Этот класс реализует интерфейс Iterable, используя Generics. Учитывая, что у вас есть элементы массива, вы сможете получить экземпляр Iterator, который является необходимым экземпляром, используемым, например, циклом foreach.

Вы можете просто создать анонимный экземпляр итератора без создания расширения Iterator и воспользоваться значением currentSize, чтобы проверить, где вы можете перемещаться по массиву (предположим, вы создали массив емкостью 10, но вы имеют только 2 элемента в 0 и 1). У экземпляра будет свой счетчик владельца, где он есть, и все, что вам нужно сделать, это сыграть с hasNext(), который проверяет, не текущее значение не равно null, и next(), которое вернет экземпляр вашего currentIndex. Ниже приведен пример использования этого API...

public static void main(String[] args) {
    // create an array of type Integer
    Integer[] numbers = new Integer[]{1, 2, 3, 4, 5};

    // create your list and hold the values.
    SOList<Integer> stackOverflowList = new SOList<Integer>(numbers);

    // Since our class SOList is an instance of Iterable, then we can use it on a foreach loop
    for(Integer num : stackOverflowList) {
        System.out.print(num);
    }

    // creating an array of Strings
    String[] languages = new String[]{"C", "C++", "Java", "Python", "Scala"};

    // create your list and hold the values using the same list implementation.
    SOList<String> languagesList = new SOList<String>(languages);

    System.out.println("");
    // Since our class SOList is an instance of Iterable, then we can use it on a foreach loop
    for(String lang : languagesList) {
        System.out.println(lang);
    }
}
// will print "12345
//C
//C++
//Java
//Python
//Scala

Если вы хотите, вы можете выполнить итерацию по нему, используя экземпляр Iterator:

// navigating the iterator
while (allNumbers.hasNext()) {
    Integer value = allNumbers.next();
    if (allNumbers.hasNext()) {
        System.out.print(value + ", ");
    } else {
        System.out.print(value);
    }
} 
// will print 1, 2, 3, 4, 5

Документация foreach находится в http://download.oracle.com/javase/1,5.0/docs/guide/language/foreach.html. Вы можете взглянуть на более полную реализацию в моей личной практике код google.

Теперь, чтобы получить эффект от того, что вам нужно, я думаю, вам нужно подключить концепцию фильтра в Iterator... Поскольку итератор зависит от следующих значений, было бы трудно вернуть true на hasNext(), а затем отфильтруйте реализацию next() со значением, которое не начинается с char "a", например. Я думаю, вам нужно играть со вторичным Interator на основе отфильтрованного списка со значениями с данным фильтром.

Ответ 3

Вы можете реализовать свой собственный итератор. Ваш итератор может быть сконструирован для обертывания Iterator, возвращаемого List, или вы можете сохранить курсор и использовать метод List get (int index). Вам просто нужно добавить логику к следующему методу Iterator, а метод hasNext - учитывать ваши критерии фильтрации. Вам также придется решить, поддерживает ли ваш итератор операцию удаления.

Ответ 4

Хороший пример для Iterable для вычисления факториала

FactorialIterable fi = new FactorialIterable(10);
Iterator<Integer> iterator = fi.iterator();
while (iterator.hasNext()){
     System.out.println(iterator.next());
}

короткий код для Java 1.8

new FactorialIterable(5).forEach(System.out::println);

пользовательский класс Iterable

public class FactorialIterable implements Iterable<Integer> {

    private final FactorialIteartor factorialIteartor;

    public FactorialIterable(Integer value) {
        factorialIteartor = new FactorialIteartor(value);
    }

    @Override
    public Iterator<Integer> iterator() {
        return factorialIteartor;
    }

    @Override
    public void forEach(Consumer<? super Integer> action) {
        Objects.requireNonNull(action);
        Integer last = 0;
        for (Integer t : this) {
            last = t;
        }
        action.accept(last);
    }

}

пользовательский класс Iterator

public class FactorialIteartor implements Iterator<Integer> {

    private final Integer mNumber;
    private Integer mPosition;
    private Integer mFactorial;


    public FactorialIteartor(Integer number) {
        this.mNumber = number;
        this.mPosition = 1;
        this.mFactorial = 1;
    }

    @Override
    public boolean hasNext() {
        return mPosition <= mNumber;
    }

    @Override
    public Integer next() {
        if (!hasNext())
            return 0;

        mFactorial = mFactorial * mPosition;

        mPosition++;

        return  mFactorial;
    }
}