2012-03-15 8 views
61

Esiste un modo rapido "computazionalmente" per ottenere il conteggio di un iteratore?Qual è il modo migliore per ottenere il conteggio/lunghezza/dimensione di un iteratore?

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

... sembra uno spreco di cicli della CPU.

+2

Un iteratore non corrisponde necessariamente a qualcosa con un "conteggio" ... –

+0

Gli iteratori sono quello che sono; per iterare al prossimo oggetto di una collezione (può essere qualcosa come set, array, ecc.) Perché hanno bisogno di dire la dimensione quando non si cura di quello che stanno cercando di ripetere? 'per fornire un metodo di accesso indipendente dall'implementazione, in cui l'utente non ha bisogno di sapere se l'implementazione sottostante è una qualche forma di array o di elenco collegato e consente all'utente di passare attraverso la raccolta senza indicizzazione esplicita. http://penguin.ewu.edu/~trolfe/LinkedSort/Iterator.html – ecle

risposta

52

Se hai appena avuto l'iteratore allora questo è quello che dovrete fare - non è così sapere quanti elementi è avuto a sinistra per itera su, quindi non puoi interrogarlo per quel risultato.

Tuttavia, molti iteratori provengono da raccolte, che è possibile richiedere spesso per la loro dimensione. E se si tratta di una classe creata dall'utente per la quale si sta utilizzando l'iteratore, si potrebbe cercare di fornire un metodo size() su quella classe.

Insomma, nella situazione in cui si solo avere l'iteratore allora non c'è modo migliore, ma molto più spesso di quanto non si ha accesso alla raccolta o oggetto sottostante da cui si può essere in grado di ottenere direttamente la dimensione .

4

Non esiste un modo più efficiente, se tutto quello che hai è l'iteratore. E se l'iteratore può essere usato solo una volta, ottenere il conteggio prima di ottenere il contenuto dell'iterizzatore è ... problematico.

La soluzione è modificare l'applicazione in modo che non sia necessario il conteggio o ottenere il conteggio con altri mezzi. (Per esempio, passare un Collection piuttosto che Iterator ...)

7

Il codice fornirà un'eccezione quando si raggiunge la fine dell'iteratore. Si potrebbe fare:

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

Se si ha avuto accesso alla raccolta sottostante, si sarebbe in grado di chiamare coll.size() ...

EDIT OK avete modificato ...

-3

iteratore oggetto contiene lo stesso numero di elementi che conteneva la tua collezione.

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

Ma invece di ottenere le dimensioni di iteratore e scorrendo indice 0 a tale dimensione, è meglio scorrere il metodo successivo() dell'iteratore.

+0

E se non avessimo "a", ma solo "i"? – Tvde1

5

Se tutto ciò che hai è l'iteratore, allora no, non c'è un modo "migliore". Se l'iteratore proviene da una collezione, potresti farlo come per le dimensioni.

Tenete a mente che Iterator è solo un'interfaccia per l'attraversamento valori distinti, si sarebbe benissimo avere codice come questo

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

Utilizzando Guava library:

int size = Iterators.size(iterator); 

Internamente, semplicemente itera su tutti gli elementi, quindi è solo per comodità.

4

Un'altra opzione è la conversione di Iterable in List.

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

Quanto sarà efficiente ??? – LoveToCode

+2

@LoveToCode Meno efficiente dell'esempio sulla domanda originale – Winter

+2

Certo, la creazione di un nuovo oggetto con tutti gli elementi è più lenta rispetto alla semplice iterazione e scartamento. IMHO, questa soluzione è un one-liner che migliora la leggibilità del codice. Lo uso molto per le raccolte con pochi elementi (fino a 1000) o quando la velocità non è un problema. – tashuhka

2

Sarà sempre necessario iterare. Eppure è possibile utilizzare Java 8, 9 per fare il conteggio senza loop esplicitamente:

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

Ecco un test:

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

Questo stampa:

5 

Abbastanza interessante è possibile parallelizzare l'operazione di conteggio qui cambiando il flag parallel in questa chiamata:

long count = StreamSupport.stream(newIterable.spliterator(), *true*).count(); 
Problemi correlati