Können wir unseren eigenen Iterator in Java schreiben?

103

Wenn ich eine Liste [alice, bob, abigail, charlie]habe, die einen Iterator enthält, und ich möchte ihn so schreiben, dass er über Elemente iteriert, die mit 'a' beginnen, kann ich dann meinen eigenen schreiben? Wie kann ich das machen ?

Phönix
quelle
4
Sie können. Sie müssen die Iterator-Schnittstelle implementieren.
GD1
Sicher, es ist nur eine normale Schnittstelle. Proxying java.util für ein JDO-Impl. beinhaltet ziemlich viele benutzerdefinierte Iteratoren.
Bests
codereview.stackexchange.com/questions/48109/…
Ciro Santilli 法轮功 冠状 病. 事件 9

Antworten:

48

Sicher. Ein Iterator ist nur eine Implementierung der java.util.IteratorSchnittstelle. Wenn Sie ein vorhandenes iterierbares Objekt (z. B. a LinkedList) von verwenden java.util, müssen Sie es entweder in Unterklassen unterteilen und seine iteratorFunktion überschreiben , damit Sie Ihr eigenes zurückgeben, oder Sie können einen Standarditerator in Ihre spezielle IteratorInstanz einschließen (welche) hat den Vorteil einer breiteren Verwendung) usw.

TJ Crowder
quelle
7
gute Antwort .... +1 Sie sind jedoch nicht gezwungen, LinkedList zu unterordnen. Sie können einen CustomIterator schreiben, der mit dem neuen CustomIterator (Somelist) instanziiert wird, da Schnittstellen nichts über Konstruktoren aussagen.
GD1
1
@Giacomo: Das habe ich mit "... oder als Mittel zum Umschließen eines Standarditerators in Ihre spezielle IteratorInstanz ..." gemeint (und danke). :-)
TJ Crowder
194

Die beste wiederverwendbare Option besteht darin, die Schnittstelle Iterable zu implementieren und den Methodeniterator () zu überschreiben.

Hier ist ein Beispiel für eine ArrayList-ähnliche Klasse, die die Schnittstelle implementiert, in der Sie die Methode Iterator () überschreiben.

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

Diese Klasse implementiert die Iterable-Schnittstelle mithilfe von Generics . Wenn Sie Elemente im Array haben, können Sie eine Instanz eines Iterators abrufen. Dies ist beispielsweise die benötigte Instanz, die von der "foreach" -Schleife verwendet wird.

Sie können einfach eine anonyme Instanz des Iterators erstellen, ohne einen erweiterten Iterator zu erstellen, und den Wert von currentSize nutzen, um zu überprüfen, bis zu welcher Stelle Sie über das Array navigieren können (nehmen wir an, Sie haben ein Array mit einer Kapazität von 10 erstellt, aber Sie haben nur 2 Elemente bei 0 und 1). Die Instanz hat ihren Eigentümerzähler, wo sie sich befindet, und alles, was Sie tun müssen, ist mit hasNext () zu spielen, das überprüft, ob der aktuelle Wert nicht null ist, und mit next (), das die Instanz Ihres currentIndex zurückgibt. Unten finden Sie ein Beispiel für die Verwendung dieser 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

Wenn Sie möchten, können Sie auch mit der Iterator-Instanz darüber iterieren:

// 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

Die foreach-Dokumentation befindet sich unter http://download.oracle.com/javase/1,5.0/docs/guide/language/foreach.html . Eine vollständigere Implementierung finden Sie in meinem persönlichen Google-Code .

Um die Auswirkungen dessen zu erhalten, was Sie benötigen, müssen Sie meines Erachtens ein Filterkonzept in den Iterator einfügen ... Da der Iterator von den nächsten Werten abhängt, ist es schwierig, true auf hasNext () und dann zurückzugeben Filtern Sie die next () - Implementierung mit einem Wert, der beispielsweise nicht mit einem Zeichen "a" beginnt. Ich denke, Sie müssen mit einem sekundären Interator herumspielen, der auf einer gefilterten Liste mit den Werten mit dem angegebenen Filter basiert.

Marcello de Sales
quelle
13
for instanceIst das ein Wortspiel?
n611x007
4
30 andere Leute dachten nicht, dass das ein Wortspiel ist :)
Marcello de Sales
2
Es wird empfohlen, nicht unterstützte Operationsausnahmen von uns implementierten Methoden auszulösen. Ich denke, es ist eine gute Idee, eine nicht unterstützte Operationsausnahme von der remove () -Methode auszulösen!
Darshan
2
Sorry @darshan, aber diese Lösung bezieht sich auf "wie man Iteratoren schreibt" ... Wenn der Fokus auf "perfekt geschriebenem Code schreiben" liegt, wäre das da!
Marcello de Sales
nicht klar, warum die Prüfung 'arrayList [currentIndex]! = null' in hasNext () erforderlich ist. kann jemand bitte erklären.
Bhushan Karmarkar
12

Gutes Beispiel für Iterable zur Berechnung der Fakultät

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

Funktionscode für Java 1.8

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

Benutzerdefinierte iterierbare Klasse

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

}

Benutzerdefinierte Iterator-Klasse

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;
    }
}
Vahe Gharibyan
quelle
8

Dies ist der vollständige Code zum Schreiben eines Iterators, sodass er über Elemente iteriert, die mit 'a' beginnen:

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

Benutzerdefinierte Iterator-Klasse

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; 
        }
    } 
}
Elvis
quelle
5

Sie können Ihren eigenen Iterator implementieren. Ihr Iterator kann so konstruiert sein, dass er den von der Liste zurückgegebenen Iterator umschließt, oder Sie können einen Cursor behalten und die Methode get (int index) der Liste verwenden. Sie müssen nur der nächsten Methode Ihres Iterators UND der hasNext-Methode Logik hinzufügen, um Ihre Filterkriterien zu berücksichtigen. Sie müssen auch entscheiden, ob Ihr Iterator den Entfernungsvorgang unterstützt.

Ditkin
quelle
1

Hier ist die vollständige Antwort auf die Frage.

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 ist der Iterator für das Array, der die Elemente zurückgibt, die mit 'a' beginnen.
  • Es ist nicht erforderlich, eine Iterable-Schnittstelle zu implementieren. Das ist aber eine Möglichkeit.
  • Dies muss nicht generisch implementiert werden.
  • Es erfüllt den Vertrag für hasNext () und next () vollständig. Wenn hasNext () angibt, dass noch Elemente vorhanden sind, gibt next () diese Elemente zurück. Und wenn hasNext () keine weiteren Elemente sagt, wird eine gültige NoSuchElementExceptionAusnahme zurückgegeben.
Apadana
quelle