Подтвердить что ты не робот

Java 8 parallelStream() с отсортированным()

JDK 8 EA сейчас нет, и я просто пытаюсь привыкнуть к lambda и новому Stream API. Я попытался отсортировать список с параллельным потоком, но результат всегда неверен:

import java.util.ArrayList;
import java.util.List;

public class Test
{
    public static void main(String[] args)
    {
        List<String> list = new ArrayList<>();
        list.add("C");
        list.add("H");
        list.add("A");
        list.add("A");
        list.add("B");
        list.add("F");
        list.add("");

        list.parallelStream() // in parallel, not just concurrently!
            .filter(s -> !s.isEmpty()) // remove empty strings
            .distinct() // remove duplicates
            .sorted() // sort them
            .forEach(s -> System.out.println(s)); // print each item
    }
}

ВЫВОД:

C
F
B
H
A

Обратите внимание, что каждый раз, когда вывод отличается. Мои вопросы, это ошибка? или невозможно сортировать список параллельно? если да, то почему JavaDoc не заявляет об этом? Последний вопрос, есть ли другая операция, выход которой будет отличаться в зависимости от типа потока?

4b9b3361

Ответ 1

Вам нужно использовать forEachOrdered, а не forEach.

В соответствии с документом forEach doc:

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

Ответ 2

Кроме того, вы можете больше узнать о parallelism и forEachOrdered с очень приятным примером из здесь. Таким образом, использование forEachOder в параллельном потоке может привести к потере преимуществ parallelism.

Вот пример из того же ресурса:

Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8 };
List<Integer> listOfIntegers =
    new ArrayList<>(Arrays.asList(intArray));

System.out.println("listOfIntegers:");
listOfIntegers
    .stream()
    .forEach(e -> System.out.print(e + " "));
System.out.println("");

System.out.println("listOfIntegers sorted in reverse order:");
Comparator<Integer> normal = Integer::compare;
Comparator<Integer> reversed = normal.reversed(); 
Collections.sort(listOfIntegers, reversed);  
listOfIntegers
    .stream()
    .forEach(e -> System.out.print(e + " "));
System.out.println("");

System.out.println("Parallel stream");
listOfIntegers
    .parallelStream()
    .forEach(e -> System.out.print(e + " "));
System.out.println("");

System.out.println("Another parallel stream:");
listOfIntegers
    .parallelStream()
    .forEach(e -> System.out.print(e + " "));
System.out.println("");

System.out.println("With forEachOrdered:");
listOfIntegers
    .parallelStream()
    .forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");

И вывод

listOfIntegers:
1 2 3 4 5 6 7 8
listOfIntegers sorted in reverse order:
8 7 6 5 4 3 2 1
Parallel stream:
3 4 1 6 2 5 7 8
Another parallel stream:
6 3 1 5 7 8 4 2
With forEachOrdered:
8 7 6 5 4 3 2 1

В пятом конвейере используется метод forEachOder, который обрабатывает элементы потока в порядке, указанном его источником, независимо от того, выполнялся ли поток последовательно или параллельно. Обратите внимание, что вы можете потерять преимущества parallelism, если используете операции, подобные forEachOrdered с параллельными потоками

.