Pourquoi Stream n'implémente-t-il pas Iterable?
en Java 8 nous avons la classe Stream , qui curieusement ont une méthode
Iterator<T> iterator()
donc vous vous attendez à ce qu'il implémente l'interface itérable , qui nécessite exactement cette méthode, mais ce n'est pas le cas.
quand je veux itérer sur un flux en utilisant une boucle foreach, je dois faire quelque chose comme
public static Iterable<T> getIterable(Stream<T> s) {
return new Iterable<T> {
@Override
public Iterator<T> iterator() {
return s.iterator();
}
};
}
for (T element : getIterable(s)) { ... }
est-ce que je manque quelque chose ici?
9 réponses
il y a déjà des gens qui demandent le même sur la liste de diffusion . La raison principale est itérable a aussi une sémantique ré-itérable, tandis que Stream ne l'est pas.
je pense que la raison principale est que
Iterableimplique la réutilisabilité, alors queStreamest quelque chose qui ne peut être utilisé qu'une seule fois - plus comme unIterator.si
StreamétenduIterablealors le code existant pourrait être surpris quand il reçoit unIterablequi lance unExceptionle c'est la deuxième fois qu'ils fontfor (element : iterable).
pour convertir un Stream en un Iterable , vous pouvez faire
Stream<X> stream = null;
Iterable<X> iterable = stream::iterator
pour passer un Stream à une méthode qui attend Iterable ,
void foo(Iterable<X> iterable)
simplement
foo(stream::iterator)
cependant, il ressemble probablement drôle; il pourrait être préférable d'être un peu plus explicite
foo( (Iterable<X>)stream::iterator );
je tiens à souligner que StreamEx ne mettre en œuvre Iterable (et Stream ), ainsi que d'une foule d'autres fonctionnalités immensément impressionnant manquant de Stream .
kennytm décrit pourquoi il est dangereux de traiter un Stream comme un Iterable , et Zhong Yu offert une solution de contournement qui permet d'utiliser un Stream comme dans Iterable , bien que d'une manière dangereuse. Il est possible d'obtenir le meilleur des deux mondes: un Iterable réutilisable à partir d'un Stream qui répond à toutes les garanties faites par la spécification Iterable .
Note: SomeType is pas un paramètre de type ici--vous devez le remplacer par un type approprié (par exemple, String ) ou le recours à la réflexion
Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):
il y a un inconvénient majeur:
les avantages de l'itération paresseuse seront perdus. Si vous avez prévu d'itérer immédiatement toutes les valeurs dans le thread courant, tous les frais généraux seront négligeables. Cependant, si vous avez prévu d'itérer seulement partiellement ou dans un fil différent, ce immédiat et complet l'itération pourrait avoir des conséquences involontaires.
Le gros avantage, bien sûr, est que vous pouvez réutiliser le Iterable , alors que (Iterable<SomeType>) stream::iterator permettrait seulement une seule utilisation. Si le code de réception sera itératif sur la collecte plusieurs fois, ce n'est pas seulement nécessaire, mais probablement bénéfique pour le rendement.
vous pouvez utiliser un flux dans une boucle for comme suit:
Stream<T> stream = ...;
for (T x : (Iterable<T>) stream::iterator) {
...
}
(Exécuter cet extrait ici )
(ceci utilise une interface fonctionnelle Java 8 cast.)
(ceci est couvert dans certains des commentaires ci-dessus (par exemple Aleksandr Dubinsky ), mais je voulais le retirer dans une réponse pour le rendre plus visible.)
Si vous n'avez pas l'esprit à l'aide de bibliothèques tierces cyclope-réagir définit un Flux de données qui implémente les deux Flux et Itératif et est rejouable trop (la résolution du problème kennytm décrit ).
Stream<String> stream = ReactiveSeq.of("hello","world")
.map(s->"prefix-"+s);
ou: -
Iterable<String> stream = ReactiveSeq.of("hello","world")
.map(s->"prefix-"+s);
stream.forEach(System.out::println);
stream.forEach(System.out::println);
[révélation je suis le développeur principal de cyclope-réagir]
n'est Pas parfait, mais les travaux:
iterable = stream.collect(Collectors.toList());
N'est pas parfait parce qu'il va chercher tous les articles du ruisseau et les mettre dans ce List , qui n'est pas exactement ce que Iterable et Stream sont environ. Ils sont censés être paresseux .
vous pouvez itérer tous les fichiers d'un dossier en utilisant Stream<Path> comme ceci:
Path path = Paths.get("...");
Stream<Path> files = Files.list(path);
for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
Object file = it.next();
// ...
}
Stream ne pas mettre en œuvre Iterable . La compréhension générale de Iterable est tout ce qui peut être itéré Sur, souvent encore et encore. Stream ne peut pas être rejouable.
la seule solution que je peux imaginer, où un itérable basé sur un flux est rejouable aussi, est de recréer le flux. J'utilise un Supplier ci-dessous pour créer une nouvelle instance de stream, chaque fois qu'un nouvel itérateur est créé.
Supplier<Stream<Integer>> streamSupplier = () -> Stream.of(10);
Iterable<Integer> iterable = () -> streamSupplier.get().iterator();
for(int i : iterable) {
System.out.println(i);
}
// Can iterate again
for(int i : iterable) {
System.out.println(i);
}