forEach против forEachOrdered в Java 8 Stream

86

Я понимаю, что эти методы различаются порядком выполнения, но во всех моих тестах я не могу добиться другого выполнения заказа.

Пример:

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

Выход:

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

Приведите примеры, когда два метода будут давать разные результаты.

gstackoverflow
источник
Попробуйте, может быть, с параллельными потоками.
Pshemo
@Pshemo это единственно возможный вариант?
gstackoverflow
5
Неуказанный порядок не означает «гарантированно другой порядок». Это просто означает « не указано» , что всегда подразумевает возможность соответствия порядку встреч. Встроенной функции перемешивания нет.
Хольгер

Ответы:

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

Вторая строка всегда будет выводить

Output:AAA
Output:BBB
Output:CCC

тогда как первый не гарантируется, так как порядок не соблюдается. forEachOrderedбудет обрабатывать элементы потока в порядке, указанном их источником, независимо от того, является ли поток последовательным или параллельным.

Цитата из forEachJavadoc:

Поведение этой операции явно недетерминировано. Для конвейеров с параллельными потоками эта операция не гарантирует соблюдение порядка встречи потока, так как это может принести в жертву преимущества параллелизма.

Когда forEachOrderedJavadoc заявляет (выделено мной):

Выполняет действие для каждого элемента этого потока в порядке встречи потока, если у потока есть определенный порядок встречи.

Тунаки
источник
6
Да ты прав. Возможно ли это только для parallelStreams?
gstackoverflow
6
Даже если бы это было применимо только к параллельным потокам прямо сейчас - а я не говорю, что это так - он все равно может сломаться в будущем, если некоторые промежуточные шаги будут оптимизированы для использования преимуществ неупорядоченных потоков, например, сортировка может использовать нестабильный алгоритм, если поток неупорядочен.
the8472
1
Значит нет смысла использовать forEachOrderedс parallel?
Bhushan
3
@BhushanPatil Да, это правильно. stackoverflow.com/questions/47336825/…
Sagar
1
Использование forEachOrdered будет обрабатывать элементы по порядку, тогда использование параллельных потоков потеряет преимущества параллелизма. Пожалуйста, предложите.
Deepak
30

Хотя он forEachкороче и выглядит красивее, я предлагаю использовать его forEachOrderedв любом месте, где важен порядок, чтобы явно указать это. Для последовательных потоков, forEachпохоже, соблюдается порядок и даже используется внутренний код API потока forEach(для потока, который, как известно, является последовательным), где это семантически необходимо использовать forEachOrdered! Тем не менее, позже вы можете решить изменить свой поток на параллельный, и ваш код будет сломан. Также, когда вы используете forEachOrderedпрограмму для чтения вашего кода, вы увидите сообщение: «Здесь важен порядок». Таким образом он лучше документирует ваш код.

Также обратите внимание, что для параллельных потоков он forEachне только выполняется в недетерминистическом порядке, но вы также можете выполнять его одновременно в разных потоках для разных элементов (что невозможно с forEachOrdered).

Наконец, оба forEach/ forEachOrderedредко полезны. В большинстве случаев вам действительно нужно получить какой-то результат, а не только побочный эффект, поэтому операции типа reduceили collectдолжны быть более подходящими. Выражение естественного сокращения с помощью forEachобычно считается плохим стилем.

Тагир Валеев
источник
7
«Наконец, оба forEach / forEachOrdered редко бывают полезными». Не могу не согласиться. Кажется, что эти методы используются слишком часто.
Tunaki
Спасибо за ответ. но это не пример из реальной жизни. Я только что изучаю java 8
gstackoverflow
Почему это семантически необходимо использовать forEachOrderedв этом коде?
RealSkeptic
1
@RealSkeptic, это указанный пользователем поток (передается в flatMap). Его можно заказать, поэтому он должен быть помещен в результирующий поток в том же порядке.
Тагир Валеев
2
@RealSkeptic, ты настоящий скептик! Stream.of("a", "b", "c").flatMap(s -> Stream.of("1", "2", "3").map(s::concat)).spliterator().hasCharacteristics(Spliterator.ORDERED)возвращает true, поэтому необходимо определить порядок результирующего потока. Если вы считаете, что документация JDK должна прямо об этом сказать, не стесняйтесь сообщать об ошибке.
Тагир Валеев
15

forEach()выполняет действие для каждого элемента этого потока. Для параллельного потока эта операция не гарантирует поддержание порядка потока.

forEachOrdered() выполняет действие для каждого элемента этого потока, гарантируя, что каждый элемент обрабатывается в порядке встречи для потоков, которые имеют определенный порядок встречи.

возьмите пример ниже:

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

Выход:

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

sushil mittal

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

mihul issltat

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

sushil mittal
Сушил Миттал
источник