Obtener el tamaño de un iterable en Java


Necesito calcular el número de elementos en un Iterable en Java. Sé que puedo hacer esto:

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

También podría hacer algo como esto, porque no necesito los objetos en el Iterable más:

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

Un punto de referencia a pequeña escala no mostró mucha diferencia de rendimiento, algún comentario u otras ideas para este problema?

Author: Philipp Wendler, 2012-07-22

9 answers

TL; DR: Usar el método utilityIterables.size(Iterable) of the great Guava library.

De sus dos fragmentos de código, debe usar el primero, porque el segundo eliminará todos los elementos de values, por lo que estará vacío después. Cambiar una estructura de datos para una consulta simple como su tamaño es muy inesperado.

Para el rendimiento, esto depende de su estructura de datos. Si es por ejemplo, de hecho un ArrayList, la eliminación de elementos desde el principio (lo que su segundo método está haciendo) es muy lento (calcular el tamaño se convierte en O (n*n) en lugar de O(n) como debería ser).

En general, si existe la posibilidad de que values sea realmente un Collection y no solo un Iterable, marque esto y llame a size() en caso de que:

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

La llamada a size() generalmente será mucho más rápida que contar el número de elementos, y este truco es exactamente lo que Iterables.size(Iterable) de Guayaba lo hace por ti.

 86
Author: Philipp Wendler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-15 20:30:23

Si está trabajando con java 8 puede usar:

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

Solo funcionará si la fuente iterable tiene un tamaño determinado. La mayoría de los Spliterators para Colecciones lo harán, pero puede tener problemas si viene de un HashSeto ResultSetpor ejemplo.

Puedes consultar el javadoc aquí.

Si Java 8 no es una opción , o si no sabes de dónde viene el iterable, puedes usar el mismo enfoque 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;
    }
 28
Author: ArnaudR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-26 09:42:39

Esto es quizás un poco tarde, pero puede ayudar a alguien. Me encuentro con un problema similar con Iterable en mi base de código y la solución fue usar for each sin llamar explícitamente a values.iterator();.

int size = 0;
for(T value : values) {
   size++;
}
 13
Author: pilot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-01 12:39:31

Estrictamente hablando, Iterable no tiene tamaño. Piense en la estructura de datos como un ciclo.

Y piensa en seguir una instancia iterable, Sin tamaño:

    new Iterable(){

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

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

                @Override
                public Object next() {
                    return fetchDataFromExternalSystem();
                }};
        }};
 6
Author: 卢声远 Shengyuan Lu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-23 04:10:52

Puede convertir su iterable a una lista y luego usarlo .tamaño() en él.

Lists.newArrayList(iterable).size();

En aras de la claridad, el método anterior requerirá la siguiente importación:

import com.google.common.collect.Lists;
 6
Author: snacks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-24 12:08:54

Apostaría por it.next() por la sencilla razón de que next() está garantizado para ser implementado, mientras que remove() es una operación opcional.

E next()

Devuelve el siguiente elemento de la iteración.

void remove()

Elimina de la colección subyacente el último elemento devuelto por el iterador (operación opcional).

 1
Author: aioobe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-22 09:12:16

En cuanto a mí, estos son solo métodos diferentes. El primero deja el objeto en el que está iterando sin cambios, mientras que los segundos lo dejan vacío. La pregunta es qué quieres hacer. La complejidad de la eliminación se basa en la implementación de su objeto iterable. Si usted está utilizando colecciones-solo obtener el tamaño como fue propuesto por Kazekage Gaara-su por lo general el mejor rendimiento enfoque sabio.

 0
Author: Mark Bramnik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-22 09:15:02

En lugar de usar bucles y contar cada elemento o usar una biblioteca de terceros, simplemente podemos encasillar el iterable en ArrayList y obtener su tamaño.

((ArrayList) iterable).size();
 0
Author: fatimasajjad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-13 07:01:20

¿Por qué no simplemente usa el método size() en su Collection para obtener el número de elementos?

Iterator es sólo para iterar, nada más.

 -1
Author: Kazekage Gaara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-22 09:10:00