Holen Sie sich die Größe eines Iterable in Java


86

Ich muss die Anzahl der Elemente in einem Iterablein Java herausfinden . Ich weiß, ich kann dies tun:

Iterable values = ...
it = values.iterator();
while (it.hasNext()) {
  it.next();
  sum++;
}

Ich könnte auch so etwas machen, weil ich die Objekte im Iterable nicht mehr brauche:

it = values.iterator();
while (it.hasNext()) {
  it.remove();
  sum++;
}

Ein kleiner Benchmark zeigte keinen großen Leistungsunterschied, keine Kommentare oder andere Ideen für dieses Problem?


Warum? Beides ist eine wirklich schlechte Idee, da beide O (N) sind. Sie sollten versuchen, zu vermeiden, dass die Sammlung zweimal wiederholt werden muss.
Marquis von Lorne

3
Dein zweiter sollte nicht einmal funktionieren. Sie können nicht anrufen, remove()ohne next()vorher anzurufen .
Louis Wasserman

Antworten:


122

TL; DR: Verwenden Sie die Utility-Methode Iterables.size(Iterable)der großen Guava- Bibliothek.

Von Ihren beiden Codefragmenten sollten Sie das erste verwenden, da das zweite alle Elemente entfernt values, sodass es danach leer ist. Das Ändern einer Datenstruktur für eine einfache Abfrage wie ihre Größe ist sehr unerwartet.

Die Leistung hängt von Ihrer Datenstruktur ab. Wenn es sich zum Beispiel tatsächlich um ein ArrayListElement handelt, ist das Entfernen von Elementen von Anfang an (was Ihre zweite Methode tut) sehr langsam (die Berechnung der Größe wird zu O (n * n) anstelle von O (n), wie es sein sollte).

Wenn die Möglichkeit besteht, dass valueses sich tatsächlich um eine Collectionund nicht nur um eine handelt Iterable, überprüfen Sie dies im Allgemeinen und rufen Sie size()an, falls:

if (values instanceof Collection<?>) {
  return ((Collection<?>)values).size();
}
// use Iterator here...

Der Aufruf size()wird in der Regel viel schneller sein als die Anzahl der Elemente zu zählen, und dieser Trick ist genau das, was Iterables.size(Iterable)von Guava für Sie tut.


Er sagt, dass er sich nicht für die Elemente interessiert und es ihm egal ist, ob sie entfernt werden.
Aioobe

Kleine Klarstellung: Es werden die Elemente ausvalues
Miquel

1
Andere Teile des Codes verwenden jedoch möglicherweise immer noch dieselbe Iterable. Und wenn nicht jetzt, vielleicht in der Zukunft.
Philipp Wendler

Ich schlage vor, die Antwort von Google Guava bekannter zu machen. Es gibt keinen Grund, die Leute dazu zu bringen, diesen Code erneut zu schreiben, obwohl er trivial ist.
Stephen Harrison

8
Wenn Sie Java 8 verwenden, erstellen Sie ein Stream- und Count-Element darin als: Stream.of (myIterable) .count ()
FrankBr

40

Wenn Sie mit Java 8 arbeiten, können Sie Folgendes verwenden:

Iterable values = ...
long size = values.spliterator().getExactSizeIfKnown();

Dies funktioniert nur, wenn die iterierbare Quelle eine bestimmte Größe hat. Die meisten Spliteratoren für Sammlungen werden, aber Sie können Probleme haben, wenn es von einem HashSetoder ResultSetzum Beispiel kommt.

Sie können den Javadoc hier überprüfen .

Wenn Java 8 keine Option ist oder Sie nicht wissen, woher das iterable stammt, können Sie den gleichen Ansatz wie Guave verwenden:

  if (iterable instanceof Collection) {
        return ((Collection<?>) iterable).size();
    } else {
        int count = 0;
        Iterator iterator = iterable.iterator();
        while(iterator.hasNext()) {
            iterator.next();
            count++;
        }
        return count;
    }

1
Jemand, gib diesem Kerl eine Medaille.
Pramesh Bajracharya

Bei Sort funktioniert das bei mir nicht . Antwort von New Bee funktioniert für mich.
Sabir Khan

17

Dies ist vielleicht etwas spät, kann aber jemandem helfen. Ich stoße auf ein ähnliches Problem Iterablein meiner Codebasis und die Lösung bestand darin, for eachohne expliziten Aufruf zu verwenden values.iterator();.

int size = 0;
for(T value : values) {
   size++;
}

2
Dies ist für mich ein intuitiver Ansatz, den ich schätzen kann. Ich habe es gerade vor ein paar Minuten benutzt. Vielen Dank.
Matt Cremeens

2
Ja, es ist intuitiv, aber leider müssen Sie eine unbenutzte Warnung für Wert unterdrücken ...
Nils-o-mat

Vielen Dank für diese Lösung, die tatsächlich die Größe des Objekts zurückgibt. Die einzige Kehrseite ist, dass Sie, wenn Sie später noch einmal durch dasselbe Objekt iterieren möchten, es zuerst irgendwie zurücksetzen sollten.
Zsolti

7

Sie können Ihre Iterable in eine Liste umwandeln und dann .size () darauf verwenden.

Lists.newArrayList(iterable).size();

Aus Gründen der Klarheit erfordert die obige Methode den folgenden Import:

import com.google.common.collect.Lists;

2
Listen ist eine Guave-Klasse
Paul Jackson

6

Genau genommen hat Iterable keine Größe. Stellen Sie sich die Datenstruktur wie einen Zyklus vor.

Und denken Sie an die folgende Iterable-Instanz: Keine Größe:

    new Iterable(){

        @Override public Iterator iterator() {
            return new Iterator(){

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

                @Override
                public Object next() {
                    return fetchDataFromExternalSystem();
                }};
        }};

2

Ich würde mich für it.next()den einfachen Grund entscheiden, der next()garantiert implementiert wird, während remove()es sich um eine optionale Operation handelt.

E next()

Gibt das nächste Element in der Iteration zurück.

void remove()

Entfernt aus der zugrunde liegenden Sammlung das letzte vom Iterator zurückgegebene Element (optionale Operation) .


Obwohl diese Antwort technisch korrekt ist, ist sie ziemlich irreführend. Das Aufrufen removewurde bereits als die falsche Methode zum Zählen von Elementen von a bezeichnet. IteratorEs spielt also keine Rolle, ob das, was wir nicht tun werden, implementiert ist oder nicht.
Stephen Harrison

1
Welcher Teil der Antwort ist irreführend? Und unter dem Umstand, remove wird implementiert, warum wäre es falsch, es zu benutzen? Übrigens werden Abstimmungen normalerweise für falsche Antworten oder Antworten verwendet, die schlechte Ratschläge geben. Ich kann nicht sehen, wie sich diese Antwort für irgendetwas davon qualifiziert.
Aioobe

2

Java 8 und höher

StreamSupport.stream(data.spliterator(), false).count();

0

Für mich sind dies nur verschiedene Methoden. Der erste lässt das Objekt, auf dem Sie iterieren, unverändert, während der zweite es leer lässt. Die Frage ist, was Sie tun möchten. Die Komplexität des Entfernens basiert auf der Implementierung Ihres iterierbaren Objekts. Wenn Sie Sammlungen verwenden - erhalten Sie einfach die Größe, wie sie von Kazekage Gaara vorgeschlagen wurde -, ist dies normalerweise der beste Ansatz in Bezug auf die Leistung.


-2

Warum verwenden Sie nicht einfach die size()Methode auf Ihrem Collection, um die Anzahl der Elemente zu ermitteln?

Iterator soll nur iterieren, sonst nichts.


4
Wer sagt, dass er eine Sammlung benutzt? :-)
aioobe

Sie verwenden einen Iterator, der das Entfernen von Elementen unterstützt. Wenn Sie die Größe vor dem Betreten der Iteratorschleife ermitteln, müssen Sie beim Entfernen vorsichtig sein und den Wert entsprechend aktualisieren.
Miquel

1
Ein Beispiel dafür, warum er möglicherweise keine Sammlung hat, um nach Größe zu suchen: Möglicherweise ruft er eine API-Methode eines Drittanbieters auf, die nur eine Iterable zurückgibt.
SteveT

2
Diese Antwort verwirrt Iteratorund Iterable. Siehe die abgestimmte Antwort für den richtigen Weg.
Stephen Harrison

-4

Anstatt Schleifen zu verwenden und jedes Element zu zählen oder eine Bibliothek eines Drittanbieters zu verwenden, können wir die Iterable einfach in ArrayList typisieren und ihre Größe ermitteln.

((ArrayList) iterable).size();

3
Nicht alle iterables können in ArrayList tho
Alexander Mills
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.