Для простых случаев, таких как иллюстрированный, они в основном одинаковы. Тем не менее, есть ряд тонких различий, которые могут быть значительными.
Одна проблема с заказом. При Stream.forEach
этом порядок не определен . Это вряд ли произойдет с последовательными потоками, тем не менее, оно находится в пределах спецификации для Stream.forEach
выполнения в произвольном порядке. Это часто происходит в параллельных потоках. Напротив, Iterable.forEach
всегда выполняется в порядке итерации Iterable
, если он указан.
Другая проблема связана с побочными эффектами. Действие , указанное в Stream.forEach
обязан быть без вмешательства . (См. Документ пакета java.util.stream .) Iterable.forEach
Потенциально имеет меньше ограничений. Для коллекций java.util
, Iterable.forEach
как правило , будут использоваться эти коллекции Iterator
, большинство из которых предназначены для быстрого отказа и которые будут выбрасываться, ConcurrentModificationException
если коллекция будет структурно изменена во время итерации. Однако модификации, которые не являются структурными , допускаются во время итерации. Например, документация класса ArrayList гласит: «Простая установка значения элемента не является структурной модификацией». Таким образом, действие дляArrayList.forEach
разрешено устанавливать значения в базовом ArrayList
без проблем.
Параллельные коллекции еще раз отличаются. Вместо быстрого отказа они разработаны как слабо последовательные . Полное определение по этой ссылке. Кратко, однако, рассмотрим ConcurrentLinkedDeque
. Действие, переданное его forEach
методу, может изменять базовую деку, даже структурно, и ConcurrentModificationException
никогда не генерируется. Тем не менее, изменения, которые происходят, могут или не могут быть видны в этой итерации. (Отсюда и «слабая» последовательность.)
Еще одно отличие видно, если Iterable.forEach
перебирает синхронизированную коллекцию. В такой коллекции один раз Iterable.forEach
принимает блокировку коллекции и удерживает ее во всех вызовах метода действия. Stream.forEach
Вызов использует spliterator своей коллекции, которая не замок, и которая опирается на преобладающих правило невмешательства. Коллекция, поддерживающая поток, может быть изменена во время итерации, и, если это так, это ConcurrentModificationException
может привести к несовместимому поведению.
Iterable.forEach takes the collection's lock
, Откуда эта информация? Я не могу найти такое поведение в источниках JDK.ArrayList
имеют довольно строгую проверку на одновременную модификацию и, следовательно, часто выбрасываютConcurrentModificationException
. Но это не гарантировано, особенно для параллельных потоков. Вместо CME вы можете получить неожиданный ответ. Рассмотрим также неструктурные модификации источника потока. Для параллельных потоков вы не знаете, какой поток будет обрабатывать конкретный элемент, и не был ли он обработан во время его изменения. Это устанавливает условие гонки, при котором вы можете получать разные результаты при каждом заезде и никогда не получать CME.Этот ответ касается производительности различных реализаций циклов. Это только незначительно актуально для циклов, которые называются ОЧЕНЬ ЧАСТО (как миллионы вызовов). В большинстве случаев содержимое цикла будет самым дорогим элементом. Для ситуаций, когда вы делаете петли действительно часто, это все еще может представлять интерес.
Вы должны повторить эти тесты в целевой системе, поскольку это зависит от конкретной реализации ( полный исходный код ).
Я запускаю openjdk версии 1.8.0_111 на быстрой машине с Linux.
Я написал тест, который зацикливается на 10 ^ 6 раз по списку, используя этот код с различными размерами для
integers
(10 ^ 0 -> 10 ^ 5 записей).Результаты приведены ниже, самый быстрый способ варьируется в зависимости от количества записей в списке.
Но все же в худших ситуациях циклическое выполнение 10 ^ 5 записей 10 ^ 6 раз занимало 100 секунд для худшего, поэтому другие соображения важнее практически во всех ситуациях.
Вот мои сроки: миллисекунды / функция / количество записей в списке. Каждый прогон составляет 10 ^ 6 петель.
Если вы повторите эксперимент, я разместил полный исходный код . Пожалуйста, отредактируйте этот ответ и добавьте результаты с пометкой протестированной системы.
Использование MacBook Pro, 2,5 ГГц Intel Core i7, 16 ГБ, macOS 10.12.6:
Java 8 Hotspot VM - 3,4 ГГц Intel Xeon, 8 ГБ, Windows 10 Pro
Java 11 Hotspot VM - 3,4 ГГц Intel Xeon, 8 ГБ, Windows 10 Pro
(тот же компьютер, что и выше, другая версия JDK)
Java 11 OpenJ9 VM - 3,4 ГГц Intel Xeon, 8 ГБ, Windows 10 Pro
(та же машина и версия JDK, что и выше, разные виртуальные машины)
Java 8 Hotspot VM - 2,8 ГГц AMD, 64 ГБ, Windows Server 2016
Java 11 Hotspot VM - 2,8 ГГц AMD, 64 ГБ, Windows Server 2016
(тот же компьютер, что и выше, другая версия JDK)
Java 11 OpenJ9 VM - 2,8 ГГц AMD, 64 ГБ, Windows Server 2016
(та же машина и версия JDK, что и выше, другая виртуальная машина)
Реализация виртуальной машины, которую вы выбираете, также имеет значение Hotspot / OpenJ9 / etc.
источник
Нет разницы между двумя упомянутыми вами понятиями, по крайней мере, концептуально,
Collection.forEach()
это просто сокращение.Внутренне
stream()
версия имеет несколько больше накладных расходов из-за создания объекта, но, смотря на время выполнения, она тоже не имеет никаких накладных расходов.Обе реализации заканчивают тем, что перебирают
collection
содержимое один раз, и во время итерации распечатывают элемент.источник
Stream
создаваемый объект или отдельные объекты? AFAIK,Stream
а не дублирует элементы.Collection.forEach () использует итератор коллекции (если он указан). Это означает, что порядок обработки элементов определен. Напротив, порядок обработки Collection.stream (). ForEach () не определен.
В большинстве случаев не имеет значения, какой из двух мы выбираем. Параллельные потоки позволяют нам выполнять поток в нескольких потоках, и в таких ситуациях порядок выполнения не определен. Java требует только завершения всех потоков перед вызовом любой терминальной операции, такой как Collectors.toList (). Давайте рассмотрим пример, в котором мы сначала вызываем forEach () непосредственно для коллекции, а затем - для параллельного потока:
Если мы запустим код несколько раз, мы увидим, что list.forEach () обрабатывает элементы в порядке вставки, а list.parallelStream (). ForEach () выдает разные результаты при каждом запуске. Один из возможных выводов:
Еще один:
источник