Чи можемо ми написати власний ітератор на Java?


104

Якщо у мене є список, який містить [alice, bob, abigail, charlie]і я хочу написати ітератор таким чином, щоб він перебирав елементи, що починаються з 'a', чи можу я написати свій? Як я можу це зробити?


4
Ти можеш. Ви повинні реалізувати інтерфейс Iterator.
gd1

звичайно, це просто звичайний інтерфейс. Проксінг java.util для імпульсу JDO. передбачає досить багато індивідуальних ітераторів.
bestsss

Відповіді:


48

Звичайно. Ітератор - це лише реалізація java.util.Iteratorінтерфейсу. Якщо ви використовуєте існуючий ітерабельний об'єкт (скажімо, а LinkedList) з java.util, вам потрібно буде або підкласифікувати його і змінити його iteratorфункцію, щоб ви повернули свій власний, або надати засіб перенесення стандартного ітератора у ваш спеціальний Iteratorекземпляр (який має перевагу в більш широкому використанні) тощо.


8
хороша відповідь .... +1 Однак ви не змушені підкласировать LinkedList. Ви можете написати CustomIterator, що він створений з новим CustomIterator (соліст), оскільки інтерфейси нічого не говорять про конструктори.
gd1

1
@Giacomo: Ось що я мав на увазі під "... або надати засоби для перенесення стандартного ітератора у ваш спеціальний Iteratorекземпляр ..." (і спасибі). :-)
TJ Crowder

196

Найкращим варіантом багаторазового використання є реалізація інтерфейсу Iterable та переопределення методу ітератора ().

Ось приклад класу 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 (), який перевіряє, чи поточне значення не є нульовим, і 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 .

Тепер, щоб отримати ефекти того, що вам потрібно, я думаю, вам потрібно підключити концепцію фільтра в ітераторі ... Оскільки ітератор залежить від наступних значень, важко буде повернути true на hasNext (), а потім фільтруйте наступну реалізацію () зі значенням, яке не починається, наприклад, з символу "a". Я думаю, вам потрібно пограти з вторинним Інтератором на основі відфільтрованого списку зі значеннями для даного фільтра.


14
for instance, це каламбур?
n611x007

4
30 інших людей не думали, що це каламбур :)
Marcello de Sales

2
Хороша практика викидати непідтримувані виключення операції з реалізованих нами методів. Я думаю, що добре викинути непідтримуваний виняток операції з методу delete ()!
даршан

2
Вибачте @darshan, але це рішення стосується "того, як писати ітератори" ... Якби акцент був на "написанні ідеально написаного коду", це було б там!
Marcello de Sales

Не зрозуміло, чому потрібно перевірити 'arrayList [currentIndex]! = null' всередині hasNext (). може хтось поясни, будь ласка.
Bhushan

12

Хороший приклад для 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 FactorialIterator factorialIterator;

    public FactorialIterable(Integer value) {
        factorialIterator = new FactorialIterator(value);
    }

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

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

}

Спеціальний клас ітератора

public class FactorialIterator implements Iterator<Integer> {

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


    public FactorialIterator(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;
    }
}

8

Це повний код для написання ітератора таким чином, щоб він перебирав елементи, що починаються з 'a':

import java.util.Iterator;

public class AppDemo {

    public static void main(String args[]) {

        Bag<String> bag1 = new Bag<>();

        bag1.add("alice");
        bag1.add("bob"); 
        bag1.add("abigail");
        bag1.add("charlie"); 

        for (Iterator<String> it1 = bag1.iterator(); it1.hasNext();) {

            String s = it1.next();
            if (s != null)
                System.out.println(s); 
        }
    }
}

Спеціальний клас ітератора

import java.util.ArrayList;
import java.util.Iterator;

public class Bag<T> {

    private ArrayList<T> data;

    public Bag() {

        data = new ArrayList<>();
    }

    public void add(T e) {

        data.add(e); 
    }

    public Iterator<T> iterator() {

        return new BagIterator();
    }

    public class BagIterator<T> implements Iterator<T> {

        private int index; 
        private String str;

        public BagIterator() {

            index = 0;
        }

        @Override
        public boolean hasNext() {

             return index < data.size();  
        }

        @Override
        public T next() {

            str = (String) data.get(index); 
            if (str.startsWith("a"))
                return (T) data.get(index++); 
            index++; 
            return null; 
        }
    } 
}

5

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


1

Ось повна відповідь на питання.

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

class ListIterator implements Iterator<String>{
    List<String> list;
    int pos = 0;

    public ListIterator(List<String> list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        while(pos < list.size()){
            if (list.get(pos).startsWith("a"))
                return true;
            pos++;
        }
        return false;

    }

    @Override
    public String next() {
        if (hasNext())
            return list.get(pos++);
        throw new NoSuchElementException();
    }
}

public class IteratorTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("alice", "bob", "abigail", "charlie");
        ListIterator itr = new ListIterator(list);

        while(itr.hasNext())
            System.out.println(itr.next()); // prints alice, abigail
    }
}
  • ListIterator є ітератором для масиву, який повертає елементи, які починаються з 'a'.
  • Немає необхідності в застосуванні Iterable інтерфейсу. Але це можливість.
  • Немає необхідності реалізовувати це загально.
  • Він повністю відповідає договору для hasNext () та next (). тобто, якщо hasNext () каже, що ще є елементи, next () поверне ці елементи. І якщо hasNext () не каже більше елементів, він повертає дійсне NoSuchElementExceptionвиключення.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.