En Java 8, nous avons la classe Stream <T> , qui possède curieusement une méthode
Iterator<T> iterator()
Vous vous attendez donc à ce qu'il implémente l'interface Iterable <T> , ce qui nécessite exactement cette méthode, mais ce n'est pas le cas.
Lorsque je veux parcourir un flux à l'aide d'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 j'ai râté quelque chose?
Il y a déjà des personnes qui ont demandé la même chose sur la liste de diffusion ☺. La raison principale est qu'itable possède également une sémantique réitérable, contrairement à Stream.
Je pense que la raison principale est que
Iterable
implique la possibilité de le réutiliser, alors queStream
est quelque chose qui ne peut être utilisé qu'une seule fois - davantage comme unIterator
.Si
Stream
extendedIterable
, le code existant peut être surpris lorsqu'il reçoit uneIterable
qui jette uneException
le deuxième fois ils fontfor (element : iterable)
.
Pour convertir une Stream
en une Iterable
, vous pouvez faire
Stream<X> stream = null;
Iterable<X> iterable = stream::iterator
Pour passer une Stream
à une méthode qui attend Iterable
,
void foo(Iterable<X> iterable)
simplement
foo(stream::iterator)
mais cela a probablement l'air drôle; il pourrait être préférable d'être un peu plus explicite
foo( (Iterable<X>)stream::iterator );
Je tiens à souligner que StreamEx
implémente Iterable
(et Stream
), ainsi qu'un ensemble d'autres fonctionnalités extrêmement géniales qui manquent dans Stream
.
kennytm a décrit pourquoi il est dangereux de traiter un Stream
comme un Iterable
, et Zhong Yu a proposé une solution de contournement qui permet d’utiliser un Stream
comme dans Iterable
, bien que de manière peu sûre. Il est possible d'obtenir le meilleur des deux mondes: une Iterable
réutilisable à partir d'une Stream
qui respecte toutes les garanties données par la spécification Iterable
.
Remarque: SomeType
n'est pas un paramètre de type ici. Vous devez le remplacer par un type approprié (par exemple, String
) ou recourir à 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 envisagez d'itérer immédiatement toutes les valeurs du thread actuel, le temps système sera négligeable. Toutefois, si vous envisagiez de n'itérer que partiellement ou dans un autre thread, cette itération immédiate et complète pourrait avoir des conséquences inattendues.
Le gros avantage, bien sûr, est que vous pouvez réutiliser la Iterable
, alors que (Iterable<SomeType>) stream::iterator
ne permettrait qu'un seul usage. Si le code de réception parcourt la collection plusieurs fois, cela est non seulement nécessaire, mais également bénéfique pour les performances.
Vous pouvez utiliser un flux dans une boucle for
comme suit:
Stream<T> stream = ...;
for (T x : (Iterable<T>) stream::iterator) {
...
}
(Run cet extrait ici )
(Cette opération utilise une conversion d'interface fonctionnelle Java 8.)
(Ceci est couvert dans certains des commentaires ci-dessus (par exemple Aleksandr Dubinsky ), mais je voulais le tirer en réponse pour le rendre plus visible.)
Stream
n'implémente pas Iterable
. La compréhension générale de Iterable
est tout ce qui peut être itéré, souvent encore et encore. Stream
n'est peut-être pas rejouable.
La seule solution de contournement à laquelle je puisse penser, lorsqu'un itératif basé sur un flux est également lisible, consiste à recréer le flux. J'utilise un Supplier
ci-dessous pour créer une nouvelle instance de flux, à 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);
}
Si cela ne vous dérange pas d'utiliser des bibliothèques tierces cyclops-react définit un flux qui implémente à la fois Stream et Iterable et qui peut être rejoué (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);
[Divulgation Je suis le développeur principal de cyclops-react]
Pas parfait, mais fonctionnera:
iterable = stream.collect(Collectors.toList());
Pas parfait parce qu'il va chercher tous les éléments du flux et les mettre dans cette List
, ce qui n'est pas exactement ce que sont Iterable
et Stream
Ils sont supposés être paresseux .
Vous pouvez parcourir 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();
// ...
}