De grootte van een Iterable in Java opvragen

Ik moet het aantal elementen in een Iterablein Java berekenen.
Ik weet dat ik dit kan:

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

Ik zou ook zoiets als dit kunnen doen, omdat ik de objecten in de Iterable verder niet nodig heb:

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

Een kleinschalige benchmark liet niet veel prestatieverschil zien, opmerkingen of andere ideeën voor dit probleem?


Antwoord 1, autoriteit 100%

TL;DR: gebruik de hulpprogrammamethode Iterables.size(Iterable)van de geweldige Guava bibliotheek.

Van je twee codefragmenten moet je de eerste gebruiken, omdat de tweede alle elementen uit valueszal verwijderen, dus deze is daarna leeg. Het wijzigen van een gegevensstructuur voor een eenvoudige zoekopdracht zoals de grootte is zeer onverwacht.

Voor prestaties hangt dit af van uw gegevensstructuur. Als het bijvoorbeeld in feite een ArrayListis, gaat het verwijderen van elementen vanaf het begin (wat je tweede methode doet) erg traag (het berekenen van de grootte wordt O(n*n) in plaats van O(n) zoals het hoort).

In het algemeen, als de kans bestaat dat valuesdaadwerkelijk een Collectionis en niet alleen een Iterable, vink dit dan aan en bel size()voor het geval:

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

De aanroep naar size()zal meestal veel sneller zijn dan het tellen van het aantal elementen, en deze truc is precies wat Iterables.size(Iterable)van Guavevoor u doet.


Antwoord 2, autoriteit 34%

Als u met java 8 werkt, kunt u het volgende gebruiken:

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

het werkt alleen als de itereerbare bron een bepaalde grootte heeft. De meeste splitsers voor verzamelingen zullen dat doen, maar u kunt problemen ondervinden als het bijvoorbeeld afkomstig is van een HashSetof ResultSet.

Je kunt de javadoc hier bekijken.

Als Java 8 geen optie is, of als je niet weet waar de iterable vandaan komt, kun je dezelfde aanpak gebruiken als guave:

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

Antwoord 3, autoriteit 14%

Dit is misschien een beetje laat, maar kan iemand helpen. Ik kom een ​​soortgelijk probleem tegen met Iterablein mijn codebase en de oplossing was om for eachte gebruiken zonder expliciet values.iterator();aan te roepen.

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

Antwoord 4, autoriteit 5%

Je kunt je iterable casten naar een lijst en er vervolgens .size() op gebruiken.

Lists.newArrayList(iterable).size();

Voor de duidelijkheid, de bovenstaande methode vereist de volgende import:

import com.google.common.collect.Lists;

Antwoord 5, autoriteit 4%

Strikt genomen heeft Iterable geen grootte. Denk aan datastructuur als een cyclus.

En denk erover na om de Iterable-instantie te volgen, geen grootte:

   new Iterable(){
        @Override public Iterator iterator() {
            return new Iterator(){
                @Override
                public boolean hasNext() {
                    return isExternalSystemAvailble();
                }
                @Override
                public Object next() {
                    return fetchDataFromExternalSystem();
                }};
        }};

Antwoord 6, autoriteit 2%

java 8 en hoger

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

Antwoord 7

Ik zou voor it.next()gaan om de eenvoudige reden dat next()gegarandeerd wordt geïmplementeerd, terwijl remove()is een optionele bewerking.

E next()

Retourneert het volgende element in de iteratie.

void remove()

Verwijdert uit de onderliggende verzameling het laatste element dat is geretourneerd door de iterator (optionele bewerking).


Antwoord 8

Wat mij betreft, dit zijn gewoon verschillende methoden. De eerste laat het object waarop je itereert ongewijzigd, terwijl de seconden het leeg laten.
De vraag is wat wil je doen.
De complexiteit van het verwijderen is gebaseerd op de implementatie van uw itereerbare object.
Als je Collecties gebruikt – neem gewoon de grootte zoals voorgesteld door Kazekage Gaara – dit is meestal de beste aanpak qua prestaties.


Antwoord 9

Waarom gebruik je niet gewoon de size()methode voor je Collectionom het aantal elementen te krijgen?

Iteratoris alleen bedoeld om te herhalen, niets anders.


Antwoord 10

In plaats van loops te gebruiken en elk element te tellen of een bibliotheek van derden te gebruiken, kunnen we eenvoudig de iterabele typen in ArrayList en de grootte ervan krijgen.

((ArrayList) iterable).size();

Other episodes