Was ist der beste Weg, um die Anzahl / Länge / Größe eines Iterators zu ermitteln?

96

Gibt es eine "rechnerisch" schnelle Möglichkeit, die Anzahl der Iteratoren zu ermitteln?

int i = 0;
for ( ; some_iterator.hasNext() ; ++i ) some_iterator.next();

... scheint eine Verschwendung von CPU-Zyklen zu sein.

Zak
quelle
2
Ein Iterator entspricht nicht unbedingt etwas mit einer "Zählung" ...
Oliver Charlesworth
Iteratoren sind was sie sind; Um zum nächsten Objekt einer Sammlung zu iterieren (es kann sich um Set, Array usw. handeln). Warum müssen sie die Größe angeben, wenn es ihnen egal ist, wofür sie iterieren möchten? to provide an implementation-independent method for access, in which the user does not need to know whether the underlying implementation is some form of array or of linked list, and allows the user go through the collection without explicit indexing. penguin.ewu.edu/~trolfe/LinkedSort/Iterator.html
Ecle

Antworten:

67

Wenn Sie nur den Iterator haben, müssen Sie dies tun - er weiß nicht , wie viele Elemente noch durchlaufen werden müssen, sodass Sie ihn nicht nach diesem Ergebnis abfragen können. Es gibt Dienstprogrammmethoden, die dies zu tun scheinen (wie Iterators.size()in Guava), aber darunter führen sie nur ungefähr den gleichen Vorgang aus.

Viele Iteratoren stammen jedoch aus Sammlungen, deren Größe Sie häufig abfragen können. Wenn es sich um eine benutzerdefinierte Klasse handelt, für die Sie den Iterator erhalten, können Sie eine size () -Methode für diese Klasse bereitstellen.

Kurz gesagt, in einer Situation, in der Sie nur den Iterator haben, gibt es keinen besseren Weg, aber häufig haben Sie Zugriff auf die zugrunde liegende Sammlung oder das zugrunde liegende Objekt, von dem Sie möglicherweise die Größe direkt abrufen können.

Michael Berry
quelle
91

Verwenden der Guavenbibliothek :

int size = Iterators.size(iterator);

Intern iteriert es nur über alle Elemente, so dass es nur der Einfachheit halber ist.

Andrejs
quelle
7
Das ist sehr elegant.
Denken
1
Dies ist nicht "rechnerisch schnell", sondern eine bequeme Methode, die den unerwünschten Nebeneffekt hat, den Iterator zu verbrauchen.
Zak
Können Sie bitte erklären, wie das funktioniert? @Andrejs List <Tuple2 <String, Integer >> wordCountsWithGroupByKey = wordsPairRdd.groupByKey () .mapValues ​​(intIterable -> Iterables.size (intIterable)). Collect (); System.out.println ("wordCountsWithGroupByKey:" + wordCountsWithGroupByKey); "Iterables.size (intIterable)?
Aditya Verma
15

Ihr Code gibt Ihnen eine Ausnahme, wenn Sie das Ende des Iterators erreichen. Du könntest es tun:

int i = 0;
while(iterator.hasNext()) {
    i++;
    iterator.next();
}

Wenn Sie Zugriff auf die zugrunde liegende Sammlung hätten, könnten Sie coll.size()...

BEARBEITEN OK Sie haben geändert ...

Assylien
quelle
Wie effizient ist das? Was ist, wenn der Iterator wie eine Million Werte ist?
Micro
4
@Micro technisch gesehen könnte ein Iterator unendlich sein - in diesem Fall wird die Schleife für immer weitergehen.
Assylias
11

Sie müssen immer iterieren. Sie können jedoch Java 8, 9 verwenden, um das Zählen durchzuführen, ohne explizit eine Schleife durchzuführen:

Iterable<Integer> newIterable = () -> iter;
long count = StreamSupport.stream(newIterable.spliterator(), false).count();

Hier ist ein Test:

public static void main(String[] args) throws IOException {
    Iterator<Integer> iter = Arrays.asList(1, 2, 3, 4, 5).iterator();
    Iterable<Integer> newIterable = () -> iter;
    long count = StreamSupport.stream(newIterable.spliterator(), false).count();
    System.out.println(count);
}

Dies druckt:

5

Interessanterweise können Sie die Zähloperation hier parallelisieren, indem Sie das parallelFlag bei diesem Aufruf ändern :

long count = StreamSupport.stream(newIterable.spliterator(), *true*).count();
gil.fernandes
quelle
7

Wenn Sie nur den Iterator haben, gibt es keinen "besseren" Weg. Wenn der Iterator aus einer Sammlung stammt, können Sie dies als Größe angeben.

Denken Sie daran, dass Iterator nur eine Schnittstelle zum Durchlaufen bestimmter Werte ist. Sie hätten sehr gut Code wie diesen

    new Iterator<Long>() {
        final Random r = new Random();
        @Override
        public boolean hasNext() {
            return true;
        }

        @Override
        public Long next() {
            return r.nextLong();
        }

        @Override
        public void remove() {
            throw new IllegalArgumentException("Not implemented");
        }
    };

oder

    new Iterator<BigInteger>() {
        BigInteger next = BigInteger.ZERO;

        @Override
        public boolean hasNext() {
            return true;
        }

        @Override
        public BigInteger next() {
            BigInteger current = next;
            next = next.add(BigInteger.ONE);
            return current;
        }

        @Override
        public void remove() {
            throw new IllegalArgumentException("Not implemented");
        }
    }; 
Roger Lindsjö
quelle
7

Bei Verwendung der Guava-Bibliothek besteht eine weitere Option darin, die Iterablein eine zu konvertieren List.

List list = Lists.newArrayList(some_iterator);
int count = list.size();

Verwenden Sie diese Option, wenn Sie nach dem Abrufen der Größe auch auf die Elemente des Iterators zugreifen müssen. Durch die Verwendung können Iterators.size()Sie nicht mehr auf die iterierten Elemente zugreifen.

Taschuhka
quelle
2
@ LoveToCode Weniger effizient als das Beispiel auf der ursprünglichen Frage
Winter
2
Sicher, das Erstellen eines neuen Objekts mit allen Elementen ist langsamer als nur das Iterieren und Verwerfen. IMHO ist diese Lösung ein Einzeiler, der die Lesbarkeit des Codes verbessert. Ich benutze es oft für Sammlungen mit wenigen Elementen (bis zu 1000) oder wenn Geschwindigkeit kein Problem ist.
Tashuhka
4

Es gibt keinen effizienteren Weg, wenn Sie nur den Iterator haben. Und wenn der Iterator nur einmal verwendet werden kann, ist es ... problematisch, die Anzahl zu ermitteln, bevor Sie den Inhalt des Iterators erhalten.

Die Lösung besteht entweder darin, Ihre Anwendung so zu ändern, dass sie nicht gezählt werden muss, oder die Zählung auf andere Weise zu erhalten. (Übergeben Sie zum Beispiel ein Collectionanstatt Iterator...)

Stephen C.
quelle
0

für Java 8 könnten Sie verwenden,

public static int getIteratorSize(Iterator iterator){
        AtomicInteger count = new AtomicInteger(0);
        iterator.forEachRemaining(element -> {
            count.incrementAndGet();
        });
        return count.get();
    }
robbie70
quelle
-5

Das Iteratorobjekt enthält die gleiche Anzahl von Elementen wie Ihre Sammlung.

List<E> a =...;
Iterator<E> i = a.iterator();
int size = a.size();//Because iterators size is equal to list a's size.

Anstatt die Größe des Iterators zu ermitteln und den Index 0 auf diese Größe zu durchlaufen, ist es besser, die Methode next () des Iterators zu durchlaufen .

Chandra Sekhar
quelle
Was ist, wenn wir nicht haben a, sondern nur i?
Tvde1