В чем разница между Collection.stream(). ForEach() и Collection.forEach()?

Я понимаю, что с .stream() я могу использовать цепные операции, например .filter(), или использовать параллельный поток. Но в чем разница между ними, если мне нужно выполнить небольшие операции (например, распечатать элементы списка)?

collection.stream().forEach(System.out::println);
collection.forEach(System.out::println);
+153
22 апр. '14 в 11:59
источник поделиться
3 ответа

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

Одна проблема заключается в заказе. При Stream.forEach порядок undefined. Это вряд ли произойдет с последовательными потоками, но в рамках спецификации для Stream.forEach выполнить в некотором произвольном порядке. Это часто происходит в параллельных потоках. Напротив, Iterable.forEach всегда выполняется в порядке итерации Iterable, если он указан.

Другая проблема связана с побочными эффектами. Действие, указанное в Stream.forEach, должно быть без вмешательства. (См. пакет java.util.stream doc.) Iterable.forEach потенциально имеет меньше ограничений. Для коллекций в java.util Iterable.forEach обычно использует эту коллекцию Iterator, большинство из которых предназначены для fail-fast и который будет бросать ConcurrentModificationException, если сборка будет структурно изменена во время итерации. Однако модификации, которые не являются структурными , разрешены во время итерации. Например, Документация класса ArrayList говорит: "Просто установка значения элемента не является структурной модификацией". Таким образом, действие для ArrayList.forEach позволяет без проблем устанавливать значения в базовом ArrayList.

Совпадающие коллекции еще раз разные. Вместо отказоустойчивости они предназначены для слабо согласованных. Полное определение находится по этой ссылке. Вкратце, однако, рассмотрим ConcurrentLinkedDeque. Действие, переданное его методу forEach , разрешено изменять базовый deque, даже структурно, и ConcurrentModificationException никогда не бросается. Однако изменение, которое происходит, может быть или не быть видимым в этой итерации. (Отсюда и "слабая" последовательность.)

Еще одно отличие видно, если Iterable.forEach выполняет итерацию по синхронизированной коллекции. В такой коллекции Iterable.forEach берет блокировку коллекции один раз и удерживает ее во всех вызовах метода действия. Вызов Stream.forEach использует разделитель сбора данных, который не блокируется и который зависит от преобладающего правила невмешательства. Сбор, поддерживающий поток, может быть изменен во время итерации, и если это так, может возникнуть ConcurrentModificationException или непоследовательное поведение.

+146
23 апр. '14 в 0:14
источник

Нет разницы между двумя упомянутыми вами, по крайней мере концептуально, Collection.forEach() является просто сокращением.

Внутренне версия stream() имеет несколько больше накладных расходов из-за создания объекта, но, глядя на время работы, у нее нет накладных расходов.

Обе реализации заканчиваются итерацией по содержимому collection один раз, а во время итерации распечатывают элемент.

+12
22 апр. '14 в 12:20
источник

Связанные вопросы


Похожие вопросы

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

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

Я запускаю openjdk версии 1.8.0_111 на быстрой машине Linux.

Я написал тест, который набирает 10 ^ 6 раз над списком, используя этот код с различными размерами для integers (10 ^ 0 → 10 ^ 5 записей).

Ниже приведены результаты, самый быстрый метод зависит от количества записей в списке.

Но все же в худших ситуациях цикл за 10 ^ 5 записей 10 ^ 6 раз составлял 100 секунд для худшего исполнителя, поэтому другие соображения важнее практически во всех ситуациях.

public int outside = 0;

private void forCounter(List<Integer> integers) {
    for(int ii = 0; ii < integers.size(); ii++) {
        Integer next = integers.get(ii);
        outside = next*next;
    }
}

private void forEach(List<Integer> integers) {
    for(Integer next : integers) {
        outside = next * next;
    }
}

private void iteratorForEach(List<Integer> integers) {
    integers.forEach((ii) -> {
        outside = ii*ii;
    });
}
private void iteratorStream(List<Integer> integers) {
    integers.stream().forEach((ii) -> {
        outside = ii*ii;
    });
}

Вот мои тайминги: миллисекунды/функция/количество записей в списке. Каждый пробег составляет 10 ^ 6 петель.

                           1    10    100    1000    10000
         for with index   39   112    920    8577    89212
       iterator.forEach   27   116    959    8832    88958
               for:each   53   171   1262   11164   111005
iterable.stream.forEach  255   324   1030    8519    88419

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

+5
24 янв. '17 в 8:27
источник

Посмотрите другие вопросы по меткам или Задайте вопрос