forEach vs forEachOrdered w Java 8 Stream

86

Rozumiem, że te metody różnią się kolejnością wykonywania, ale we wszystkich moich testach nie mogę osiągnąć innej realizacji zamówienia.

Przykład:

System.out.println("forEach Demo");
Stream.of("AAA","BBB","CCC").forEach(s->System.out.println("Output:"+s));
System.out.println("forEachOrdered Demo");
Stream.of("AAA","BBB","CCC").forEachOrdered(s->System.out.println("Output:"+s));

Wynik:

forEach Demo
Output:AAA
Output:BBB
Output:CCC
forEachOrdered Demo
Output:AAA
Output:BBB
Output:CCC

Proszę podać przykłady, kiedy 2 metody dadzą różne wyniki.

gstackoverflow
źródło
Może spróbuj z równoległymi strumieniami.
Pshemo
@Pshemo czy to jedyna możliwa opcja?
gstackoverflow
5
Nieokreślona kolejność nie oznacza „gwarantowanej innej kolejności”. Oznacza to po prostu nieokreślony , co zawsze oznacza możliwość dopasowania kolejności spotkań. Nie ma wbudowanej funkcji odtwarzania losowego.
Holger,

Odpowiedzi:

89
Stream.of("AAA","BBB","CCC").parallel().forEach(s->System.out.println("Output:"+s));
Stream.of("AAA","BBB","CCC").parallel().forEachOrdered(s->System.out.println("Output:"+s));

Druga linia zawsze będzie wyświetlana

Output:AAA
Output:BBB
Output:CCC

podczas gdy pierwsza nie jest gwarantowana, ponieważ zamówienie nie jest dotrzymywane. forEachOrderedprzetwarza elementy strumienia w kolejności określonej przez źródło, niezależnie od tego, czy strumień jest sekwencyjny czy równoległy.

Cytat z forEachJavadoc:

Zachowanie tej operacji jest wyraźnie niedeterministyczne. W przypadku potoków ze strumieniem równoległym ta operacja nie gwarantuje przestrzegania kolejności spotkań strumienia, ponieważ spowodowałoby to utratę korzyści z równoległości.

Kiedy forEachOrderedJavadoc stwierdza (podkreślenie moje):

Wykonuje akcję dla każdego elementu tego strumienia, w kolejności napotkania strumienia, jeśli strumień ma zdefiniowaną kolejność spotkań.

Tunaki
źródło
6
Tak masz rację. Czy jest to możliwe tylko w przypadku strumieni równoległych?
gstackoverflow
6
Nawet jeśli miałoby to zastosowanie tylko do strumieni równoległych w tej chwili - i nie mówię, że tak jest - nadal może się zepsuć w przyszłości, jeśli niektóre kroki pośrednie zostaną zoptymalizowane w celu wykorzystania nieuporządkowanych strumieni, np. Sortowanie może używać niestabilnego algorytmu, jeśli strumień jest nieuporządkowany.
the8472
1
Więc nie ma sensu używać forEachOrderedz parallel?
Bhushan
3
@BhushanPatil Tak, zgadza się. stackoverflow.com/questions/47336825/…
Sagar
1
Użycie forEachOrdered spowoduje przetworzenie elementu według kolejności, a użycie równoległych strumieni spowoduje utratę korzyści z równoległości. Proszę zasugeruj.
Deepak
30

Chociaż jest forEachkrótszy i wygląda ładniej, sugerowałbym użycie go forEachOrderedw każdym miejscu, w którym ma znaczenie, aby wyraźnie to określić. W przypadku strumieni sekwencyjnych forEachwydaje się, że szanuje kolejność, a nawet wykorzystuje wewnętrzny kod API strumienia forEach(dla strumienia, o którym wiadomo, że jest sekwencyjny), gdzie jest to semantycznie konieczne forEachOrdered! Niemniej jednak możesz później zdecydować się na zmianę strumienia na równoległy, a kod zostanie uszkodzony. Również podczas korzystania forEachOrderedz czytnika Twojego kodu zobaczysz komunikat: „tu liczy się kolejność”. W ten sposób lepiej dokumentuje twój kod.

Należy również zauważyć, że dla strumieni równoległych forEachnie tylko jest wykonywany w kolejności niedetenistycznej, ale można go również wykonywać jednocześnie w różnych wątkach dla różnych elementów (co nie jest możliwe w przypadku forEachOrdered).

Wreszcie oba forEach/ forEachOrderedsą rzadko przydatne. W większości przypadków faktycznie potrzebujesz jakiegoś wyniku, a nie tylko efektu ubocznego, dlatego operacje takie jak reducelub collectpowinny być bardziej odpowiednie. Wyrażanie operacji redukcji z natury za pomocą forEachjest zwykle uważane za zły styl.

Tagir Valeev
źródło
7
„Wreszcie obie forEach / forEachOrdered są rzadko przydatne”. Nie mogłem się bardziej zgodzić. Wygląda na to, że te metody są nadużywane.
Tunaki
Dziękuję za odpowiedź. ale to nie jest przykład z prawdziwego życia. Właśnie uczę się java 8
gstackoverflow
Dlaczego użycie forEachOrderedw tym kodzie jest semantycznie konieczne ?
RealSkeptic
1
@RealSkeptic, jest to strumień określony przez użytkownika (przekazywany do flatMap). Można go zamówić, więc należy go umieścić w otrzymanym strumieniu w tej samej kolejności.
Tagir Valeev
2
@RealSkeptic, jesteś prawdziwym sceptykiem! Stream.of("a", "b", "c").flatMap(s -> Stream.of("1", "2", "3").map(s::concat)).spliterator().hasCharacteristics(Spliterator.ORDERED)zwraca wartość true, dlatego należy określić kolejność wynikowego strumienia. Jeśli uważasz, że dokumentacja JDK powinna wyraźnie o tym mówić, możesz zgłosić błąd.
Tagir Valeev
15

forEach()wykonuje akcję dla każdego elementu tego strumienia. W przypadku strumienia równoległego ta operacja nie gwarantuje utrzymania kolejności strumienia.

forEachOrdered() metoda wykonuje akcję dla każdego elementu tego strumienia, gwarantując, że każdy element jest przetwarzany w kolejności napotkania dla strumieni, które mają zdefiniowaną kolejność spotkań.

weźmy poniższy przykład:

    String str = "sushil mittal";
    System.out.println("****forEach without using parallel****");
    str.chars().forEach(s -> System.out.print((char) s));
    System.out.println("\n****forEach with using parallel****");

    str.chars().parallel().forEach(s -> System.out.print((char) s));
    System.out.println("\n****forEachOrdered with using parallel****");

    str.chars().parallel().forEachOrdered(s -> System.out.print((char) s));

Wynik:

****forEach without using parallel****

sushil mittal

****forEach with using parallel****

mihul issltat

****forEachOrdered with using parallel****

sushil mittal
Sushil Mittal
źródło