Domanda

Perché l'interfaccia Iterator non si estende Iterable?

Il metodo iterator() potrebbe semplicemente restituire this.

È apposta o solo una supervisione dei progettisti di Java?

Sarebbe conveniente poter usare un ciclo for-each con iteratori come questo:

for(Object o : someContainer.listSomeObjects()) {
    ....
}

dove listSomeObjects() restituisce un iteratore.

È stato utile?

Soluzione

Perché un iteratore in genere punta a una singola istanza in una raccolta. Iterable implica che uno può ottenere un iteratore da un oggetto per attraversare i suoi elementi - e non c'è bisogno di iterare su una singola istanza, che è ciò che rappresenta un iteratore.

Altri suggerimenti

Un iteratore è con stato. L'idea è che se chiami Iterable.iterator() due volte otterrai iteratori indipendenti - per la maggior parte degli iterabili, comunque. Questo chiaramente non sarebbe il caso nel tuo scenario.

Ad esempio, di solito posso scrivere:

public void iterateOver(Iterable<String> strings)
{
    for (String x : strings)
    {
         System.out.println(x);
    }
    for (String x : strings)
    {
         System.out.println(x);
    }
}

Questo dovrebbe stampare la collezione due volte, ma con il tuo schema il secondo ciclo terminerebbe sempre all'istante.

Per i miei $ 0,02, sono completamente d'accordo sul fatto che Iterator non dovrebbe implementare Iterable, ma penso che anche il potenziato per loop dovrebbe accettare. Penso che l'intero & Quot; renda iteratori iterabili & Quot; l'argomento si presenta come una soluzione a un difetto nella lingua.

L'intero motivo dell'introduzione del ciclo for avanzato è stato che " elimina la fatica e la propensione all'errore degli iteratori e delle variabili di indice durante l'iterazione di raccolte e matrici " [ 1 ].

Collection<Item> items...

for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
    Item item = iter.next();
    ...
}

for (Item item : items) {
    ...
}

Perché allora questo stesso argomento non vale per gli iteratori?

Iterator<Iter> iter...
..
while (iter.hasNext()) {
    Item item = iter.next();
    ...
}

for (Item item : iter) {
    ...
}

In entrambi i casi, le chiamate a hasNext () e next () sono state rimosse e non vi è alcun riferimento all'iteratore nel ciclo interno. Sì, capisco che gli Iterabili possono essere riutilizzati per creare più iteratori, ma tutto ciò avviene al di fuori del ciclo for: all'interno del ciclo c'è sempre e solo una progressione in avanti di un elemento alla volta sugli elementi restituiti dall'iteratore.

Inoltre, consentire ciò semplificherebbe anche l'uso del ciclo for per le enumerazioni, che, come è stato sottolineato altrove, sono analoghi agli Iteratori e non agli Iterabili.

Quindi non rendere Iterator implementare Iterable, ma aggiorna il ciclo for per accettarlo.

Saluti,

Come sottolineato da altri, Iterator e < a href = "http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"> Iterable sono due cose diverse.

Inoltre, <=> le implementazioni precedenti sono migliorate per i loop.

È anche banale superare questa limitazione con un semplice metodo adattatore che assomiglia a questo se usato con importazioni di metodi statici:

for (String line : in(lines)) {
  System.out.println(line);
}

Implementazione di esempio:

  /**
   * Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
   * loops. If {@link Iterable#iterator()} is invoked more than once, an
   * {@link IllegalStateException} is thrown.
   */
  public static <T> Iterable<T> in(final Iterator<T> iterator) {
    assert iterator != null;
    class SingleUseIterable implements Iterable<T> {
      private boolean used = false;

      @Override
      public Iterator<T> iterator() {
        if (used) {
          throw new IllegalStateException("SingleUseIterable already invoked");
        }
        used = true;
        return iterator;
      }
    }
    return new SingleUseIterable();
  }

In Java 8 l'adattamento di <=> a <=> diventa più semplice:

for (String s : (Iterable<String>) () -> iterator) {

Come altri hanno già detto, un Iterable può essere chiamato più volte, restituendo un Iterator fresco ad ogni chiamata; un Iteratore viene usato solo una volta. Quindi sono correlati, ma servono a scopi diversi. Frustrante, tuttavia, il & Quot; compatto per & Quot; Il metodo funziona solo con un iterabile.

Quello che descriverò di seguito è un modo per avere il meglio di entrambi i mondi: restituire un Iterable (per una sintassi migliore) anche quando la sequenza di dati sottostante è una tantum.

Il trucco è restituire un'implementazione anonima di Iterable che in realtà avvia il lavoro. Quindi, invece di fare il lavoro che genera una sequenza una tantum e poi restituire un Iteratore su quello, si restituisce un Iterable che, ogni volta che si accede, ripete il lavoro. Potrebbe sembrare dispendioso, ma spesso chiamerai l'Iterable solo una volta comunque, e anche se lo chiami più volte, ha comunque una semantica ragionevole (a differenza di un semplice wrapper che rende un Iterator & Quot; assomiglia a " un Iterable, questo non fallirà se usato due volte).

Ad esempio, supponiamo che io abbia un DAO che fornisce una serie di oggetti da un database e voglio fornire accesso a quello tramite un iteratore (ad es. per evitare di creare tutti gli oggetti in memoria se non sono necessari). Ora potrei semplicemente restituire un iteratore, ma ciò rende brutto l'utilizzo del valore restituito in un ciclo. Quindi invece avvolgo tutto in un Iterable anon:

class MetricDao {
    ...
    /**
     * @return All known metrics.
     */
    public final Iterable<Metric> loadAll() {
        return new Iterable<Metric>() {
            @Override
            public Iterator<Metric> iterator() {
                return sessionFactory.getCurrentSession()
                        .createQuery("from Metric as metric")
                        .iterate();
            }
        };
    }
}

questo può quindi essere usato in codice come questo:

class DaoUser {
    private MetricDao dao;
    for (Metric existing : dao.loadAll()) {
        // do stuff here...
    }
}

che mi permette di usare il compact per loop mantenendo comunque l'uso incrementale della memoria.

Questo approccio è " lazy " - il lavoro non viene svolto quando viene richiesto l'Iterable, ma solo successivamente quando i contenuti vengono ripetuti - e devi essere consapevole delle conseguenze di ciò. Nell'esempio con un DAO che significa scorrere i risultati all'interno della transazione del database.

Quindi ci sono vari avvertimenti, ma questo può essere ancora un utile linguaggio in molti casi.

Incredibilmente, nessun altro ha ancora dato questa risposta. Ecco come puoi & Quot; facilmente & Quot; iterare su Iterator usando il nuovo Java 8 Iterator.forEachRemaining() metodo:

Iterator<String> it = ...
it.forEachRemaining(System.out::println);

Certo, c'è un " " più semplice; soluzione che funziona direttamente con il ciclo foreach, racchiudendo Iterable in <=> lambda:

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);

Iterator è un'interfaccia che ti consente di scorrere su qualcosa. È un'implementazione di muoversi attraverso una raccolta di qualche tipo.

Iterable è un'interfaccia funzionale che indica che qualcosa contiene un iteratore accessibile.

In Java8, questo semplifica la vita ... Se hai un <=> ma hai bisogno di un <=> puoi semplicemente fare:

Iterator<T> someIterator;
Iterable<T> = ()->someIterator;

Questo funziona anche nel for-loop:

for (T item : ()->someIterator){
    //doSomething with item
}

Vedo anche molti fare questo:

public Iterator iterator() {
    return this;
}

Ma questo non lo rende giusto! Questo metodo non sarebbe quello che vuoi!

Il metodo iterator() dovrebbe restituire un nuovo iteratore a partire da zero. Quindi è necessario fare qualcosa del genere:

public class IterableIterator implements Iterator, Iterable {

  //Constructor
  IterableIterator(IterableIterator iter)
  {
    this.initdata = iter.initdata;
  }
  // methods of Iterable

  public Iterator iterator() {
    return new MyClass(this.somedata);
  }

  // methods of Iterator

  public boolean hasNext() {
    // ...
  }

  public Object next() {
    // ...
  }

  public void remove() {
    // ...
  }
}

La domanda è: ci sarebbe un modo per fare una lezione astratta in questo modo? In modo che per ottenere un IterableIterator basta implementare i due metodi next () e hasNext ()

Se sei venuto qui in cerca di una soluzione alternativa, puoi utilizzare IteratorIterable . (disponibile per Java 1.6 e versioni successive)

Esempio di utilizzo (invertendo un vettore).

import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
    public static void main(String ... args) {
        Vector<String> vs = new Vector<String>();
        vs.add("one");
        vs.add("two");
        for ( String s: vs ) {
            System.out.println(s);
        }
        Iterable<String> is
            = new IteratorIterable(new ReverseListIterator(vs));
        for ( String s: is ) {
            System.out.println(s);
        }
    }
}

stampe

one
two
two
one

Sono d'accordo con la risposta accettata, ma voglio aggiungere la mia spiegazione.

  • Iteratore rappresenta lo stato di attraversamento, ad esempio è possibile ottenere l'elemento corrente da un iteratore e passare al successivo.

  • Iterable rappresenta una raccolta che potrebbe essere attraversata, può restituire tutti gli iteratori che desideri, ognuno dei quali rappresenta il proprio stato di attraversamento, un iteratore può indicare il primo elemento, mentre un altro può indicare il 3 ° elemento.

Sarebbe bello se Java per loop accetta sia Iterator che Iterable.

Per semplicità, Iteratore e Iterable sono due concetti distinti, Iterable è semplicemente una scorciatoia per " Posso restituire un Iterator " ;. Penso che il tuo codice dovrebbe essere:

for(Object o : someContainer) {
}

con un'istanza someContainer di SomeContainer extends Iterable<Object>

A parte: Scala ha un metodo toIterable () in Iterator. Vedi conversione implicita o esplicita scala da iteratore a iterabile

In una nota correlata, potresti trovare utile l'adattatore IteratorIterable in Apache Commons Collections4. Basta creare un'istanza da un iteratore e hai l'iterabile corrispondente.

https: // commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html

ID: org.apache.commons: commons-collections4: 4.0

Gli iteratori sono stateful, hanno un " next " elemento e diventa " esaurito " una volta ripetuto. Per vedere dove si trova il problema, eseguire il codice seguente, quanti numeri sono stampati?

Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);

Puoi provare il seguente esempio:

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top