Почему java8 потоков потребителя и метод? - PullRequest
5 голосов
/ 06 января 2020

Я новичок в Java 8, просто хочу спросить, в чем разница в производительности между двумя фрагментами кода ниже.

Я знаю, что оба работают, но мне интересно, почему команда Java создала метод Consumer::andThen, когда я могу использовать обычный подход.

List<String> list = Arrays.asList("aaa","cccc","bbbb");
List<String> list2 = new ArrayList<>();

// Подход 1

list.stream().forEach(x -> {
            list2.add(x);
            System.out.println(x);
        });

// Подход 2

Consumer<String> c1 = s -> list2.add(s);
Consumer<String> c2 = s -> System.out.println(s);

list.stream().forEach(c1.andThen(c2));

ИМО, подход 1 лучше, почему подхода2 снова? Если оба они одинаковы, то почему создан метод andThen () ? Я предполагаю, что должна быть причина, которую они создали.

Другими словами, когда именно метод andThen() действительно полезен?

Ответы [ 2 ]

11 голосов
/ 06 января 2020

Я согласен, что не имеет смысла создавать отдельный экземпляр Consumer<String> для каждого оператора только для использования andThen.

Однако, если у вас уже есть два Consumer<String> экземпляра (c1 и c2), и вам нужно выполнить оба, вы можете написать:

list.forEach(x -> {
        c1.accept(x);
        c2.accept(x);
    });

или

list.forEach(c1.andThen(c2));

В этом случае последнее явно чище.

РЕДАКТИРОВАТЬ:

Я хотел бы расширить на случай, когда вы создаете свои собственные Consumer с (то есть не повторно использовать уже существующие). Когда вы присваиваете лямбда-выражение переменной Consumer, вы можете использовать этот экземпляр Consumer в нескольких местах вместо дублирования лямбда-выражения. Это верно для любого функционального интерфейса.

Как только вы решите использовать переменную Consumer, решение о том, иметь ли Consumer, реализующий сложные логики c или 2 более простых Consumer с, каждая реализующая часть логики c зависит от того, есть ли необходимость в некоторых случаях выполнять только часть логики c (или изменять порядок, в котором выполняются части логики c) .

Наличие c1 и c2 позволяет использовать одну из следующих опций:

list.forEach(c1);
list.forEach(c2);
list.forEach(c1.andThen(c2));
list.forEach(c2.andThen(c1));

Если у вас был один потребитель, который выполняет лог c обоих c1 и c2, у вас меньше гибкости.

3 голосов
/ 06 января 2020

Как примечание, возможен другой подход:

Подход 3

list.stream()
        .peek(System.out::println)
        .forEach(list2::add);

или даже лучше:

List<String> list2 = list.stream()
        .peek(System.out::println)
        .collect(Collectors.toList());

С точки зрения производительности все подходы сопоставимы, но последний является наиболее «функциональным». Проблема с предыдущими - вставка во второй список в потоке. Это побочный эффект, который считается плохой практикой. С этой точки зрения, подход с двумя потребителями является худшим, на мой взгляд, поскольку побочный эффект является наиболее скрытым.

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