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
Iterable
implique la réutilisabilité, alors queStream
est quelque chose qui ne peut être utilisé qu'une seule fois - plus comme unIterator
.si
Stream
étenduIterable
alors le code existant pourrait être surpris quand il reçoit unIterable
qui lance unException
le 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);
}