Dlaczego Stream nie implementuje Iterowalnego?

W Javie 8 mamy klasę Stream, która co ciekawe posiada metodę

Iterator<T> iterator()

Więc można oczekiwać, że zaimplementuje interfejs Iterable, który wymaga dokładnie tej metody, ale tak nie jest.

Kiedy chcę iterować nad strumieniem za pomocą pętli foreach, muszę zrobić coś w stylu

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)) { ... }
Czy coś mi umknęło?
Author: Stuart Marks, 2013-11-21

9 answers

Są już osoby pytane o to samo na liście dyskusyjnej ☺. Głównym powodem jest Iterable ma również re-iterable semantic, podczas gdy Stream nie jest.

Myślę, że głównym powodem jest to, że Iterable implikuje możliwość ponownego użycia, podczas gdy Stream jest czymś, co może być użyte tylko raz - bardziej jak Iterator.

Jeśli Stream Rozszerzony Iterable wtedy istniejący kod może być zaskoczony, gdy otrzyma Iterable, który rzuca Exception drugi raz robią for (element : iterable).

 163
Author: kennytm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-21 19:26:45

Aby przekonwertować Stream na Iterable, możesz wykonać

Stream<X> stream = null;
Iterable<X> iterable = stream::iterator

Aby przekazać Stream do metody, która oczekuje Iterable,

void foo(Iterable<X> iterable)

Po prostu

foo(stream::iterator) 

Jednak prawdopodobnie wygląda to śmiesznie; może lepiej być trochę bardziej wyraźne

foo( (Iterable<X>)stream::iterator );
 136
Author: ZhongYu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-08 21:12:42

Chciałbym zwrócić uwagę, że StreamEx implementuje Iterable (i Stream), a także wiele innych niezmiernie niesamowitych funkcji, których brakuje w Stream.

 10
Author: Aleksandr Dubinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-30 16:16:47

Kennytm opisał dlaczego jest niebezpieczne traktowanie {[1] } jako Iterable, a Zhong Yu zaoferował obejście , które pozwala na używanie Stream Jak w Iterable, aczkolwiek w sposób niebezpieczny. Możliwe jest uzyskanie tego, co najlepsze z obu światów: wielokrotnego użytku Iterable z Stream, który spełnia wszystkie gwarancje zawarte w specyfikacji Iterable.

Uwaga: SomeType nie jest tu parametrem typu-należy go zastąpić odpowiednim typem (np. odbicie

Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):

Jest jedna zasadnicza wada:

Korzyści płynące z leniwej iteracji zostaną utracone. Jeśli planujesz natychmiast iterować wszystkie wartości w bieżącym wątku, wszelkie koszty ogólne będą znikome. Jeśli jednak planujesz iterację tylko częściowo lub w innym wątku, ta natychmiastowa i kompletna iteracja może mieć niezamierzone konsekwencje.

Dużą zaletą jest oczywiście to, że możesz ponownie użyć Iterable, podczas gdy (Iterable<SomeType>) stream::iterator pozwoli tylko jednorazowego użytku. Jeśli otrzymujący kod będzie wielokrotnie powtarzał kolekcję, jest to nie tylko konieczne, ale prawdopodobnie korzystne dla wydajności.

 6
Author: Zenexer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-30 16:25:01

Możesz użyć strumienia w pętli for w następujący sposób:

Stream<T> stream = ...;

for (T x : (Iterable<T>) stream::iterator) {
    ...
}

(Uruchom Ten fragment tutaj)

(używa interfejsu funkcjonalnego Java 8 cast.)

(jest to omówione w niektórych komentarzach powyżej (np.Aleksandr Dubinsky ), ale chciałem wyciągnąć to w odpowiedzi, aby było bardziej widoczne.)

 3
Author: Rich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-27 10:09:15

Jeśli nie masz nic przeciwko użyciu bibliotek stron trzecich cyclops-react definiuje strumień, który implementuje zarówno Stream, jak i Iterable i jest również odtwarzalny (rozwiązując problem opisany przez kennytma ).

 Stream<String> stream = ReactiveSeq.of("hello","world")
                                    .map(s->"prefix-"+s);

Lub:-

 Iterable<String> stream = ReactiveSeq.of("hello","world")
                                      .map(s->"prefix-"+s);

 stream.forEach(System.out::println);
 stream.forEach(System.out::println);

[Disclosure I am the lead developer of cyclops-react]

 1
Author: John McClean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:23

Nie idealne, ale będzie działać:

iterable = stream.collect(Collectors.toList());

Nie jest idealny, ponieważ pobiera wszystkie przedmioty ze strumienia i umieszcza je w tym List, co nie jest dokładnie tym, o czym są Iterable i Stream. Mają być leniwi .

 0
Author: yegor256,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-07 04:47:48

Możesz iterować wszystkie pliki w folderze używając Stream<Path> w następujący sposób:

Path path = Paths.get("...");
Stream<Path> files = Files.list(path);

for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
    Object file = it.next();

    // ...
}
 0
Author: BullyWiiPlaza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-23 21:12:37

Stream nie implementuje Iterable. Ogólne zrozumienie Iterable jest wszystkim, co może być powtarzane, często raz po raz. Stream może nie być odtwarzane.

Jedynym obejściem, jakie przychodzi mi do głowy, gdy iterowalny strumień oparty na strumieniu jest również odtwarzalny, jest odtworzenie strumienia. Używam Supplier poniżej, aby utworzyć nową instancję stream, za każdym razem, gdy tworzony jest nowy iterator.

    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
Author: Ashish Tyagi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-21 05:40:24