66 votes

Est un "infini" itérateur mauvaise conception?

Il est généralement considéré comme une mauvaise pratique à offrir Iterator des implémentations qui sont "à l'infini"; c'est à dire où les appels à l' hasNext() (*) return true?

En général, je dirais "oui", parce que le code appelant pouvait se comporter de façon erratique, mais dans le dessous de la mise en œuvre hasNext() renvoie la valeur true à moins que l'appelant supprime tous les éléments de la Liste que l'itérateur a été initialisé avec; c'est à dire il y a une condition de résiliation. Vous pensez que c'est une utilisation légitime de la Iterator? Il ne semble pas violer le contrat, bien que je suppose que l'on pourrait faire valoir qu'il est peu intuitive.

public class CyclicIterator<T> implements Iterator<T> {
  private final List<T> l;
  private Iterator<T> it;

  public CyclicIterator<T>(List<T> l) {
    this.l = l;
    this.it = l.iterator();
  }

  public boolean hasNext() {
    return !l.isEmpty();
  }

  public T next() {
    T ret;

    if (!hasNext()) {
      throw new NoSuchElementException();
    } else if (it.hasNext()) {
      ret = it.next();
    } else {
      it = l.iterator();
      ret = it.next();
    }

    return ret;
  }

  public void remove() {
    it.remove();
  }
}

(Pédant) MODIFIER

Certaines personnes ont commenté la façon dont un Iterator pourrait être utilisé pour générer des valeurs à partir d'une surabondance de la séquence telle que la suite de Fibonacci. Cependant, la Java Iterator documentation, un Itérateur est:

Un itérateur sur une collection.

Maintenant, on pourrait dire que la suite de Fibonacci est une collection infinie, mais en Java je équivaudrait collection avec l' java.util.Collection interface, qui propose des méthodes telles que l' size() ce qui signifie que la collection doit être limitée. Donc, est-il légitime d'utiliser Iterator comme un générateur de valeurs à partir d'une surabondance de la séquence?

76voto

oxbow_lakes Points 70013

Je pense que c'est tout à fait légitime - Iterator est juste un courant de "trucs". Pourquoi le flux nécessairement être limité?

Beaucoup d'autres langues (par exemple, la Scala), le concept d'une surabondance de flux intégré à eux, et ceux-ci peuvent être itéré. Par exemple, à l'aide de scalaz

scala> val fibs = (0, 1).iterate[Stream](t2 => t2._2 -> (t2._1 + t2._2)).map(_._1).iterator
fibs: Iterator[Int] = non-empty iterator

scala> fibs.take(10).mkString(", ") //first 10 fibonnacci numbers
res0: String = 0, 1, 1, 2, 3, 5, 8, 13, 21, 34

EDIT: En termes de principe de moindre surprise, je pense que tout dépend du contexte. Par exemple, que serais-je attendre de cette méthode pour le retour?

public Iterator<Integer> fibonacciSequence();

19voto

Jörg W Mittag Points 153275

Le point de l'ensemble de l' Iterator , c'est qu'il est paresseux, c'est à dire qu'il ne vous donne qu'autant d'objets que vous demandez. Si un utilisateur demande pour tous les objets d'une infinie Iterator,, c'est leur problème, pas le vôtre.

7voto

Joachim Sauer Points 133411

Alors moi aussi, je pense que c'est légitime, je tiens à ajouter qu'un tel Iterator (ou plus précisément: un Iterable production d'un tel Iterator) ne serait pas de bien jouer avec le renforcement de la boucle for (un.k.pour chaque boucle):

for (Object o : myIterable) {
   ...
}

Depuis que le code à l'intérieur d'une amélioration pour la boucle a pas d'accès direct à l'itérateur, il ne pouvait pas appeler à l' remove() sur l'itérateur.

Donc à la fin de la boucle il aurait à faire l'une des opérations suivantes:

  • Obtenez de l'accès à l'interne, List de la Iterator et de supprimer des objets directement, éventuellement provoquer un ConcurrentModificationException
  • utiliser break à la sortie de la boucle
  • "utiliser" Exception à la sortie de la boucle
  • utiliser return sortir de la boucle

Tous, mais le dernier de ces solutions de rechange ne sont pas exactement la meilleure façon de laisser une amélioration pour la boucle.

Le "normal" pour la boucle (ou tout autre boucle avec un explicite Iterator variable) serait très bien fonctionner, bien sûr:

for (Iterator it = getIterator(); it.hasNext();) {
  Object o = it.next()
  ...
}

7voto

Marcus Adams Points 27070

Cela peut être de la sémantique, mais l'itérateur doit diligemment retour à l'élément suivant, sans égard à la fin. Fin est un effet secondaire d'une collection, même si il peut sembler comme un effet secondaire fréquent.

Encore, quelle est la différence entre l'infini, et une collection de 10 billions de dollars des éléments? L'appelant veut tous, ou il ne l'est pas. L'appelant de décider comment le nombre d'éléments à retourner ou à la fin.

Je ne dirais pas que l'appelant ne pouvait pas l'utiliser pour chaque construction. Il pourrait, aussi longtemps qu'il veut tous les éléments.

Quelque chose dans la documentation de la collection comme "peut-être une collection infinie" serait appropriée, mais pas "infini itérateur".

4voto

Yuval Adam Points 59423

Il est parfaitement légitime d'utiliser - tant qu'il est correctement documenté.

En utilisant le nom de l' CyclicIterator est une bonne idée, car elle en déduit que l'on boucle sur l'itérateur pourrait bien éventuellement être infinie si la sortie de boucle cas n'est pas correctement défini.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X