108 votes

Obtenir la taille d'un Iterable en Java

J'ai besoin de calculer le nombre d'éléments d'un fichier Iterable en Java. Je sais que je peux le faire :

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

Je pourrais également faire quelque chose comme ceci, car je n'ai plus besoin des objets de l'Iterable :

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

Un benchmark à petite échelle n'a pas montré une grande différence de performance, des commentaires ou d'autres idées pour ce problème ?

146voto

Philipp Wendler Points 4590

TL;DR : Utilisez la méthode de l'utilité Iterables.size(Iterable) du grand Goyave bibliothèque.

De vos deux extraits de code, vous devriez utiliser le premier, car le second supprimera tous les éléments de l'application values donc il est vide après. Changer une structure de données pour une simple requête comme sa taille est très inattendu.

Pour les performances, cela dépend de votre structure de données. Si, par exemple, il s'agit en fait d'une ArrayList le retrait d'éléments depuis le début (ce que fait votre deuxième méthode) est très lent (le calcul de la taille devient O(n*n) au lieu de O(n) comme il devrait l'être).

En général, s'il y a une chance que values est en fait un Collection et pas seulement un Iterable Vérifiez ceci et appelez size() dans le cas :

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

L'appel à size() sera généralement beaucoup plus rapide que de compter le nombre d'éléments, et cette astuce est exactement ce qu'il faut faire. Iterables.size(Iterable) de Goyave fait pour vous.

52voto

ArnaudR Points 1787

Si vous travaillez avec java 8, vous pouvez utiliser :

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

cela ne fonctionnera que si la source itérable a une taille déterminée. La plupart des Spliterators pour les collections fonctionnent, mais vous pouvez avoir des problèmes s'ils proviennent d'un fichier HashSet o ResultSet par exemple.

Vous pouvez vérifier le javadoc ici.

Si Java 8 n'est pas une option ou si vous ne savez pas d'où vient l'itérable, vous pouvez utiliser la même approche que guava :

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

19voto

pilot Points 816

C'est peut-être un peu tard, mais cela peut aider quelqu'un. J'ai rencontré un problème similaire avec Iterable dans ma base de code et la solution était d'utiliser for each sans appeler explicitement values.iterator(); .

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

8voto

snacks Points 129

Vous pouvez convertir votre itérable en une liste puis utiliser .size() sur celle-ci.

Lists.newArrayList(iterable).size();

Pour des raisons de clarté, la méthode ci-dessus nécessitera l'importation suivante :

import com.google.common.collect.Lists;

7voto

Strictement parlant, Iterable n'a pas de taille. Pensez à la structure de données comme à un cycle.

Et pensez à l'instance suivante d'Iterable, sans taille :

    new Iterable(){

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

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X