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?

221
demandé sur Stuart Marks 2013-11-21 23:06:22

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 que Stream est quelque chose qui ne peut être utilisé qu'une seule fois - plus comme un Iterator .

si Stream étendu Iterable alors le code existant pourrait être surpris quand il reçoit un Iterable qui lance un Exception le c'est la deuxième fois qu'ils font for (element : iterable) .

163
répondu kennytm 2013-11-21 19:26:45

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 );
136
répondu ZhongYu 2016-12-08 21:12:42

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 .

10
répondu Aleksandr Dubinsky 2015-05-30 16:16:47

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.

6
répondu Zenexer 2017-08-30 16:25:01

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

3
répondu Rich 2017-06-27 10:09:15

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]

1
répondu John McClean 2017-05-23 12:18:23

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 .

0
répondu yegor256 2017-01-07 04:47:48

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

    // ...
}
0
répondu BullyWiiPlaza 2017-12-23 21:12:37

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);
    }
0
répondu Ashish Tyagi 2018-04-21 05:40:24