Согласно документации на веб-сайте Oracle [...]
Эта ссылка для Java 8. Возможно, вы захотите прочитать документацию по Java 9 (вышла в 2017 году) и более поздние версии, так как они более явные в этом отношении. В частности:
Реализация потока допускает значительную свободу действий при оптимизации вычисления результата. Например, потоковая реализация может свободно исключать операции (или целые этапы) из потокового конвейера - и, следовательно, разрешать вызов поведенческих параметров - если она может доказать, что это не повлияет на результат вычисления. Это означает, что побочные эффекты поведенческих параметров не всегда могут выполняться и на них не следует полагаться, если не указано иное (например, в операциях терминала forEach
и forEachOrdered
). (Для конкретного c примера такой оптимизации см. Примечание API, документированное для операции count()
. Подробнее см. В разделе побочные эффекты потока пакет документации.)
Источник: Java 9's Javado c для интерфейса Stream
.
А также обновленная версия do c, которую вы цитировали:
Побочные эффекты
Побочные эффекты в поведенческих параметрах для потоковых операций, как правило, не приветствуются, так как они часто могут привести к невольным нарушениям требования о безгражданстве, а также другим угрозам безопасности потока.
Если у поведенческих параметров действительно есть побочные эффекты, если явно не указано, нет никаких гарантий относительно :
- видимость этих побочных эффектов для других потоков;
- то, что различные операции над "одним и тем же" элементом в одном и том же потоковом конвейере выполняются в одном и том же потоке; и
- , что поведенческие параметры всегда вызываются, поскольку реализация потока может свободно исключать операции (или целые этапы) из конвейера потока, если она может доказать, что это не повлияет на результат вычисления.
Порядок побочных эффектов может быть удивительным. Даже когда конвейер ограничен для получения результата, который согласуется с порядком встречи с источником потока (например, IntStream.range(0,5).parallel().map(x -> x*2).toArray()
должен произвести [0, 2, 4, 6, 8]
), никаких гарантий относительно порядка, в котором применяется функция отображения, не дается. для отдельных элементов или в каком потоке любой поведенческий параметр выполняется для данного элемента.
Исключение побочных эффектов также может вызывать удивление. За исключением операций терминала forEach
и forEachOrdered
, побочные эффекты поведенческих параметров могут не всегда выполняться, когда реализация потока может оптимизировать выполнение поведенческих параметров, не влияя на результат вычисления , (Для конкретного примера c см. Примечание API, документированное для операции count
.)
Источник: Java 9 Javado c для java.util.stream
package .
Весь акцент мой.
Как видите, в официальной документации current более подробно рассматриваются вопросы, которые вы можете столкнуться, если вы решите использовать побочные эффекты в ваших потоковых операциях. Также очень ясно, что forEach
и forEachOrdered
являются единственными терминальными операциями, где выполнение побочных эффектов гарантировано (учтите, что проблемы с безопасностью потоков все еще существуют, как показывают официальные примеры).
При этом, учитывая ваш указанный c код и только указанный код:
public List<SavedCars> saveCars(List<Car> cars) {
return cars.stream()
.map(this::saveCar)
.collect(Collectors.toList());
}
Я не вижу проблем, связанных с потоками, с указанным кодом как есть.
- Шаг
.map()
будет выполнен, потому что .collect()
(операция изменчивое сокращение , которую советник c рекомендует вместо таких вещей, как .forEach(list::add)
) полагается на .map()
и, поскольку это (то есть Вывод saveCar()
s отличается от его ввода, поток не может "доказать, что [eliding] , это не повлияет на результат вычисления" . - Это не
parallelStream()
, поэтому он не должен вызывать проблем параллелизма, которых раньше не было (конечно, если кто-то добавил .parallel()
позже, тогда могут возникнуть проблемы - например, если кто-то решит распараллелить for
l oop путем запуска новых потоков для внутренних вычислений).
Это не означает, что в этом примере используется код Good Code ™. Последовательность .stream.map(::someSideEffect()).collect()
как способ выполнения операций с побочными эффектами для каждого элемента в коллекции может выглядеть более простой / короткой / элегантной? чем его for
аналог, и иногда это может быть. Однако, как сказали вам Юджин, Хольгер и некоторые другие, есть более эффективные способы подойти к этому.
В качестве быстрой мысли: стоимость запуска Stream
против итерации простого for
не будет незначительной, если у вас нет лот элементов, и если у вас есть лот элементов, то вы: a) вероятно, не хотите создавать новый доступ к БД для каждого, поэтому saveAll(List items)
API будет лучше; и б), вероятно, не хотят испытывать снижение производительности при последовательной обработке лота элементов, поэтому вам придется использовать распараллеливание, и тогда возникает целый новый набор проблем.