В Java 8 обеспечивается ли последовательный и упорядоченный поток, гарантирующий выполнение операций в порядке сбоя?

Есть ли какая-либо гарантия того, что операции над последовательным и упорядоченным потоком обработаны в порядке встречи?

Я имею в виду, если у меня есть такой код:

IntStream.range(0, 5)
        .map(i -> {
            myFunction(i);
            return i * 2;
         })
         .boxed()
         .collect(toList());

есть ли гарантия, что он выполнит вызовы myFunction() в порядке выполнения сгенерированного диапазона?

Я нашел проект класса JavaDocs для потока, который прямо указывает на это:

Для последовательных поточных конвейеров все операции выполняются в порядке вызова источника конвейера, если источник конвейера имеет определенный порядок встреч.

но в официальном JavaDocs эта строка была удалена. Теперь он обсуждает порядок встреч только для выбранных методов. Пакет java.util.stream doc в разделе Побочные эффекты:

Даже когда конвейер ограничен для получения результата, который согласуется с порядком встречи источника потока (например, IntStream.range(0,5).parallel().map(x -> x*2).toArray() должен производить [0, 2, 4, 6, 8]), никаких гарантий не делается относительно порядка, в котором преобразователь функция применяется к отдельным элементам или в каком потоке выполняется какой-либо поведенческий параметр для данного элемента.

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

С другой стороны, он также указывает в разделе Ordering:

Если поток упорядочен, большинство операций ограничено для работы с элементами в их порядке встречи; если источником потока является List, содержащий [1, 2, 3], тогда результат выполнения map(x -> x*2) должен быть [2, 4, 6]. Однако, если источник не имеет определенного порядка регистрации, то любая перестановка значений [2, 4, 6] будет действительным результатом.

но на этот раз он начинается с "работы с элементами", но пример относится к результирующему потоку, поэтому я не уверен, что они принимают побочные эффекты в учетной записи, а побочные эффекты - это действительно то, о чем этот вопрос.

Ответ 1

Я думаю, что мы можем многому научиться из того, что это явное предложение было удалено. Этот вопрос, по-видимому, тесно связан с вопросом "Does Stream.forEach уважают порядок очередей последовательных потоков?" . Ответ от Брайана Гетца в основном говорит о том, что, несмотря на то, что не существует сценария, когда порядок игнорируется текущей реализацией потоков, когда forEach вызывается в последовательном потоке, forEach имеет право игнорировать порядок встреч даже для последовательных потоков по каждой спецификации.

Теперь рассмотрим следующий раздел Stream документация классов:

Для выполнения вычислений потоковые операции объединяются в конвейер потока. Конвейер потока состоит из источника (который может быть массивом, коллекцией, функцией генератора, каналом ввода-вывода и т.д.), Ноль или более промежуточных операций (которые преобразуют поток в другой поток, например filter(Predicate)), и операцию терминала (которая дает результат или побочный эффект, например count() или forEach(Consumer)). Потоки ленивы; вычисление исходных данных выполняется только при инициировании операции терминала, а исходные элементы потребляются только по мере необходимости.

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

Обратите внимание, что это может сделать не только forEach. A Collector имеет возможность сообщать о UNORDERED характеристике, например. Collectors.toSet() не зависит от порядка встреч. Очевидно, что также операция типа count() не зависит от порядка в Java 9, она может даже вернуться без какой-либо обработки элементов. Подумайте о IntStream#sum() для другого примера.

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

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