Question

Pourquoi l'interface Iterator ne s'étend-t-elle pas Iterable?

La méthode iterator() peut simplement renvoyer this.

Est-ce délibérément ou simplement un oubli des concepteurs de Java?

Il serait pratique de pouvoir utiliser une boucle for each avec des itérateurs tels que:

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

listSomeObjects() renvoie un itérateur.

Était-ce utile?

La solution

Parce qu'un itérateur pointe généralement sur une seule instance dans une collection. Iterable implique que l'on puisse obtenir un itérateur d'un objet à parcourir sur ses éléments - et il n'est pas nécessaire de parcourir plusieurs occurrences, ce que représente un itérateur.

Autres conseils

Un itérateur est à état. L'idée est que si vous appelez Iterable.iterator() deux fois, vous obtiendrez des itérateurs indépendants - pour la plupart des itérables, en tout cas. Ce ne serait clairement pas le cas dans votre scénario.

Par exemple, je peux généralement écrire:

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

Cela devrait imprimer la collection deux fois - mais avec votre schéma, la seconde boucle se terminerait toujours instantanément.

Pour mon prix de 0,02 $, je suis tout à fait d’accord pour qu’Iterator ne mette pas en œuvre Iterable, mais je pense que la boucle for améliorée devrait accepter l’une ou l’autre. Je pense que l'ensemble & "Rend les itérateurs itérables &"; l’argument est un problème autour de la langue.

L’introduction de la boucle for améliorée s’expliquait par le fait qu'elle & "élimine la corvée et la prédisposition aux erreurs des itérateurs et des variables d’index lors d’une itération sur des collections et des tableaux &"; [ 1 ].

Collection<Item> items...

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

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

Pourquoi alors ce même argument ne s'applique-t-il pas aux itérateurs?

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

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

Dans les deux cas, les appels à hasNext () et next () ont été supprimés et il n'y a aucune référence à l'itérateur dans la boucle interne. Oui, je comprends que Iterables peut être réutilisé pour créer plusieurs itérateurs, mais que tout se passe en dehors de la boucle for: à l'intérieur de la boucle, il n'y a jamais qu'une progression avant sur un élément à la fois par rapport aux éléments renvoyés par l'itérateur. / p>

En outre, autoriser cette opération faciliterait également l’utilisation de la boucle for pour les énumérations, qui, comme il a été indiqué ailleurs, sont analogues à des itérateurs et non à des itérats.

Donc, ne faites pas qu'Iterator implémente Iterable, mais mettez à jour la boucle for pour accepter soit.

A bientôt,

Comme d'autres l'ont souligné, Iterator et < a href = "http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"> Iterable sont deux choses différentes.

De même, <=> les implémentations sont antérieures aux boucles for améliorées.

Il est également facile de surmonter cette limitation avec un adaptateur simple qui ressemble à ceci lorsqu'il est utilisé avec des importations de méthodes statiques:

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

Exemple d'implémentation:

  /**
   * 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();
  }

Sous Java 8, l’adaptation d'un <=> à un <=> devient plus simple:

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

Comme d'autres l'ont déjà dit, un itérateur peut être appelé plusieurs fois, renvoyant un nouvel itérateur à chaque appel; un itérateur est utilisé une seule fois. Donc, ils sont liés, mais servent des objectifs différents. Frustrant cependant, le & Quot; compact pour & Quot; Cette méthode ne fonctionne qu'avec un itératif.

Ce que je vais décrire ci-dessous est une façon d'obtenir le meilleur des deux mondes: renvoyer un Iterable (pour une syntaxe plus agréable) même lorsque la séquence de données sous-jacente est unique.

Le truc consiste à renvoyer une implémentation anonyme d'Iterable qui déclenche le travail. Ainsi, au lieu de faire le travail qui génère une séquence unique et de renvoyer ensuite un Iterator par dessus, vous renvoyez un Iterable qui, chaque fois que vous y accédez, refait le travail. Cela peut sembler une perte de temps, mais souvent vous n’appelerez l’Iterable qu’une seule fois, et même si vous l’appelez plusieurs fois, il possède toujours une sémantique raisonnable (contrairement à un simple wrapper qui fait ressembler un Iterator & À & "un Iterable, cela n'échouera pas s'il est utilisé deux fois).

Par exemple, disons que je dispose d’un DAO qui fournit une série d’objets d’une base de données et que je souhaite y accéder via un itérateur (par exemple, pour éviter de créer tous les objets en mémoire s’ils ne sont pas nécessaires). Maintenant, je pourrais simplement renvoyer un itérateur, mais cela rend l’utilisation de la valeur renvoyée dans une boucle moche. Alors, au lieu de cela, je range tout dans un Iterable:

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

cela peut alors être utilisé dans un code comme ceci:

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

qui me permet d’utiliser la boucle for compact tout en conservant une utilisation incrémentielle de la mémoire.

Cette approche est " paresseuse " - le travail n'est pas terminé lorsque l'Iterable est demandé, mais seulement plus tard lorsque le contenu est itéré - et vous devez être conscient des conséquences. Dans l'exemple avec un DAO, cela signifie parcourir les résultats dans la transaction de base de données.

Donc, il y a diverses mises en garde, mais cela peut toujours être un idiome utile dans de nombreux cas.

Incroyablement, personne d’autre n’a encore répondu à cette question. Voici comment vous pouvez & "Facilement &"; itérer sur un Iterator en utilisant le nouveau Java 8 Iterator.forEachRemaining() méthode:

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

Bien sûr, il existe un " plus simple " solution fonctionnant directement avec la boucle foreach, en enveloppant Iterable dans un <=> lambda:

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

Iterator est une interface qui vous permet de parcourir quelque chose. C’est une implémentation consistant à se déplacer dans une collection quelconque.

Iterable est une interface fonctionnelle qui indique que quelque chose contient un itérateur accessible.

En Java8, cela facilite la vie ... Si vous avez un <=> mais avez besoin d'un <=>, vous pouvez simplement faire:

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

Cela fonctionne aussi dans la boucle for:

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

Je vois aussi beaucoup faire cela:

public Iterator iterator() {
    return this;
}

Mais cela ne le rend pas juste! Cette méthode ne serait pas ce que vous voulez!

La méthode iterator() est supposée renvoyer un nouvel itérateur à partir de zéro. Donc, il faut faire quelque chose comme ça:

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 question est la suivante: y aurait-il un moyen de faire une classe abstraite exécutant ceci? Pour obtenir un IterableIterator, il suffit d'implémenter les deux méthodes next () et hasNext ()

.

Si vous êtes venu ici à la recherche d'une solution de contournement, vous pouvez utiliser IteratorIterable . (disponible pour Java 1.6 et supérieur)

Exemple d'utilisation (inversion d'un vecteur).

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

imprime

one
two
two
one

Je suis d'accord avec la réponse acceptée, mais je souhaite ajouter ma propre explication.

  • Iterator représente l’état de la traversée. Par exemple, vous pouvez obtenir l’élément actuel à partir d’un itérateur et passer au suivant.

  • Iterable représente une collection pouvant être parcourue. Il peut renvoyer autant d'itérateurs que vous le souhaitez, chacun représentant son propre état de déplacement. Un itérateur peut pointer vers le premier élément, un autre peut pointer vers le 3ème élément.

Il serait bien que Java for loop accepte à la fois Iterator et Iterable.

Par souci de simplicité, Iterator et Iterable sont deux concepts distincts, Iterable est simplement un raccourci pour & "Je peux renvoyer un Iterator &". Je pense que votre code devrait être:

for(Object o : someContainer) {
}

avec une instance de ContentContainer SomeContainer extends Iterable<Object>

En passant: Scala utilise une méthode toIterable () dans Iterator. Voir la conversion implicite ou explicite scala d'itérateur en itérable .

Sur une note connexe, vous pourrez trouver l’adaptateur IteratorIterable dans Apache Commons Collections4 utile. Créez simplement une instance à partir d'un itérateur, et vous obtenez l'itérable correspondant.

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

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

Les itérateurs sont à état, ils ont un & "next &"; élément et devenir " épuisé " une fois itéré sur. Pour voir où se situe le problème, exécutez le code suivant, combien de numéros sont imprimés?

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

Vous pouvez essayer l'exemple suivant:

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top