Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: xdevel2000Ответов: 2

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

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

javajava-8java-stream

Ответы

Ответ 1 · рейтинг 18 · Keppil

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

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

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

...

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

Ответ 2 · рейтинг 11 · Luigi Rubino

Вы используете 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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы