2012-03-15 8 views
61

¿Existe una forma "computacionalmente" rápida de obtener el conteo de un iterador?¿Cuál es la mejor manera de obtener el recuento/longitud/tamaño de un iterador?

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

... parece una pérdida de ciclos de CPU.

+2

Un iterador no corresponde necesariamente a algo con un "recuento" ... –

+0

Iteradores son lo que son; para iterar al siguiente objeto de una colección (puede ser cualquier cosa como conjunto, matriz, etc.) ¿Por qué necesitan decir el tamaño cuando no les importa para qué están tratando de iterar? 'proporcionar un método de acceso independiente de la implementación, en el cual el usuario no necesita saber si la implementación subyacente es alguna forma de matriz o de lista enlazada, y permite al usuario examinar la colección sin una indexación explícita. http://penguin.ewu.edu/~trolfe/LinkedSort/Iterator.html – ecle

Respuesta

52

Si usted acaba de conseguir el iterador entonces eso es lo que tendrá que hacer - no lo hace saber número de elementos que le queda a iterar, por lo que no puede consultarlo para ese resultado.

Sin embargo, muchos iteradores provienen de colecciones, que a menudo puede consultar para su tamaño. Y si es una clase hecha por el usuario para la que está obteniendo el iterador, podría intentar proporcionar un método de tamaño() en esa clase.

En resumen, en la situación en la que única tener el iterador entonces no hay mejor manera, pero mucho más a menudo que no tienen acceso a la colección u objeto subyacente de la que usted puede ser capaz de obtener el tamaño directamente .

4

No existe una manera más eficiente, si todo lo que tiene es el iterador. Y si el iterador solo puede usarse una vez, obtener el conteo antes de obtener los contenidos del iterador es ... problemático.

La solución es cambiar su aplicación para que no necesite el conteo u obtener el recuento por otros medios. (Por ejemplo, pasar una Collection en lugar de Iterator ...)

7

Su código le dará una excepción cuando llegue al final del iterador. Se podría hacer:

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

Si tuviera acceso a la colección subyacente, que sería capaz de llamar coll.size() ...

EDITAR bien que haya modificado ...

-3

iterador objeto contiene la misma cantidad de elementos que su colección contenía.

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

Pero en vez de conseguir el tamaño de iterador y la iteración a través de índice de 0 a ese tamaño, es mejor para recorrer el método siguiente() del iterador.

+0

¿Qué pasa si no tenemos 'a', pero solo' i'? – Tvde1

5

Si todo lo que tiene es el iterador, entonces no, no hay una "mejor" manera. Si el iterador proviene de una colección, puede hacerlo para el tamaño.

Tenga en cuenta que iterador es sólo una interfaz para atravesar valores distintos, que sería muy bien tener un código como este

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"); 
     } 
    }; 

o

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"); 
     } 
    }; 
71

Usando Guava library:

int size = Iterators.size(iterator); 

Internamente solo itera sobre todos los elementos por lo que es solo por conveniencia.

4

Otra opción es convertir Iterable a List.

int count = Lists.newArrayList(some_iterator).size(); 
+1

¿Cuánto será eficiente? – LoveToCode

+2

@LoveToCode Menos eficiente que el ejemplo de la pregunta original – Winter

+2

Claro, crear un nuevo objeto con todos los elementos es más lento que simplemente iterar y descartar. En mi humilde opinión, esta solución es de una sola línea que mejora la legibilidad del código. Lo uso mucho para colecciones con pocos elementos (hasta 1000) o cuando la velocidad no es un problema. – tashuhka

2

Siempre tendrá que repetir. Sin embargo, se puede usar Java 8, 9 para hacer el recuento y sin bucle explícitamente:

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

Aquí está una prueba:

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); 
} 

Esta impresora:

5 

lo suficientemente interesantes que se pueden paralelizar la operación de recuento aquí cambiando el indicador parallel en esta llamada:

long count = StreamSupport.stream(newIterable.spliterator(), *true*).count(); 
Cuestiones relacionadas