Поток упорядоченных/неупорядоченных проблем

У меня есть следующий код:

Set<Integer> l = new TreeSet<>();
l.add(1);
l.add(10);
l.add(3);
l.add(-3);
l.add(-4);

и я хочу упорядочить коллекцию с помощью:

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

но forEach всегда возвращает заказанную коллекцию!

Тогда у меня есть еще одно сомнение относительно приведенного ниже предложения из здесь:

Для последовательных потоков наличие или отсутствие порядка встречи не влияет на производительность, а только на детерминированность. Если поток упорядочен, повторное выполнение идентичных потоковых конвейеров на идентичном источнике даст идентичный результат; если он не упорядочен, повторное выполнение может привести к другим результатам.

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

Arrays.stream( new int[]{8, -1, 3}).forEach(System.out::println);
Arrays.stream( new int[]{8, -1, 3}).forEach(System.out::println);

Я действительно не понимаю эту часть API...


person xdevel2000    schedule 25.01.2014    source источник


Ответы (2)


Операция unordered() не выполняет никаких действий для явной неупорядоченности потока. Что он делает, так это то, что он снимает ограничение на поток, который должен оставаться упорядоченным, тем самым позволяя последующим операциям использовать оптимизации, которые не должны учитывать упорядочение.

Вы можете прочитать об этом в документах по Java 8. :

Для последовательных потоков наличие или отсутствие порядка встречи не влияет на производительность, а только на детерминированность. Если поток упорядочен, повторное выполнение идентичных потоковых конвейеров на идентичном источнике даст идентичный результат; если он не упорядочен, повторное выполнение может привести к другим результатам.
Для параллельных потоков ослабление ограничения упорядочения иногда может обеспечить более эффективное выполнение.

...

В случаях, когда поток имеет порядок встреч, но пользователь не особенно заботится об этом порядке встреч, явное изменение порядка потоков с помощью unordered() может улучшить параллельную производительность для некоторых операций с отслеживанием состояния или терминальных операций.

person Keppil    schedule 25.01.2014
comment
Да, но в документе написано: некоторые промежуточные операции, такие как sorted(), могут налагать порядок встречи на неупорядоченный в противном случае поток, а другие могут отображать упорядоченный поток неупорядоченным, например BaseStream.unordered() На самом деле, если я вызову отсортированный по несортированной коллекции, он сортирует... - person xdevel2000; 25.01.2014
comment
Конечно, но отсортированный поток по-прежнему действителен для передачи в качестве неупорядоченного потока, просто он не дает никаких гарантий по этому поводу. - person Keppil; 25.01.2014
comment
поэтому, чтобы снять ограничения по порядку, мы используем методы unorderd() и foreach()? если да, то почему у нас есть 2 способа сделать одно и то же? и это даже ничего не значит, когда вы работаете с последовательным потоком - person amarnath harish; 24.08.2018

Вы используете TreeSet, который упорядочивает элементы. Итак, элементами будут:

-4, -3, 1, 3, 10

Использование последовательного потока не меняет порядок, поэтому результат не зависит от свойства потока быть упорядоченным/неупорядоченным, как вы можете видеть в первом примере "Последовательный поток" (и как вы сами заметили).

l.stream().map(s->s+" ").forEach(System.out::print);

Результат: -4 -3 1 3 10

l.stream().unordered().map(s->s+" ").forEach(System.out::print);

Результат: -4 -3 1 3 10

Если вы сделаете поток параллельным, то может быть создано более одного потока, и результат больше не гарантируется, поскольку он зависит от конкретного выполнения. Также обратите внимание, что forEach() не является упорядоченной операцией, что означает, что она выполняется всякий раз, когда в ее конвейере есть что-то для обработки. См. пример «Неупорядоченные операции над параллельным потоком»:

l.stream().parallel().map(s->s+" ").forEach(System.out::print);

Результат: 3 10 -3 1 -4

l.stream().unordered().map(s->s+" ").parallel().forEach(System.out::print);

Результат: 3 -4 -3 1 10

Все меняется, когда вы используете упорядоченные операции (такие как findFirst(), limit() и skip()) в параллельном потоке. Когда вы используете findFirst() в конвейере, вы действительно хотите иметь первый элемент, в соответствии с порядком элементов в потоке, вам просто не нужен какой-либо элемент, и в этом случае вы будете использовать findAny(). Чтобы получить детерминированные результаты этих операций, вы теряете преимущества использования параллельного потока, потому что выполнение должно быть сериализовано для обработки элементов в определенном порядке.

   l.stream().parallel().skip(2).limit(2).findFirst().ifPresent(System.out::print);

Результат: 1 . Этот результат всегда будет одинаковым, независимо от того, сколько раз вы выполняете код.

l.stream().unordered().parallel().skip(2).limit(2).findFirst().ifPresent(System.out::print);

Результат: -3 . Этот результат может (а может и не меняться) каждый раз при выполнении кода, потому что мы указали, что порядок не важен.

Вот полный код:

public static void main(String[] args) {
    Set<Integer> l = new TreeSet<>();
    l.add(1);
    l.add(10);
    l.add(3);
    l.add(-3);
    l.add(-4);

    System.out.println("Serial Stream");
    l.stream().map(s->s+" ").forEach(System.out::print);
    System.out.println();
    l.stream().unordered().map(s->s+" ").forEach(System.out::print);
    System.out.println("\n");

    System.out.println("Unordered Operations on a Parallel Stream");
    l.stream().parallel().map(s->s+" ").forEach(System.out::print);
    System.out.println();
    l.stream().unordered().map(s->s+" ").parallel().forEach(System.out::print);
    System.out.println("\n");

    System.out.println("Ordered Operations on a Parallel Stream");
    l.stream().parallel().skip(2).limit(2).findFirst().ifPresent(System.out::print);
    System.out.println();
    l.stream().unordered().parallel().skip(2).limit(2).findFirst().ifPresent(System.out::print);
    System.out.println("\n");

}

Серийный поток

-4 -3 1 3 10

-4 -3 1 3 10

Неупорядоченные операции над параллельным потоком

3 10 -3 -4 1

-3 1 3 10 -4

Упорядоченные операции над параллельным потоком

1

-3

person Luigi Rubino    schedule 26.06.2016
comment
так что нет возможности видеть разные результаты каждый раз, когда вы запускаете код, когда вы работаете с последовательным потоком? - person amarnath harish; 24.08.2018