Метод Stream.peek() в Java 8 vs Java 9

Я участвую в процессе обучения с помощью ямбда-выражений Java 8 и хотел бы спросить о следующем фрагменте кода Java, относящемся к методу peek в интерфейсе функций, с которым я столкнулся.

При выполнении программы на IDE она не выводит. Я ожидал, что это даст 2, 4, 6.

import java.util.Arrays;
import java.util.List;

public class Test_Q3 {

    public Test_Q3() {
    }

    public static void main(String[] args) {
        List<Integer> values = Arrays.asList(1, 2, 3);
        values.stream()
              .map(n -> n * 2)
              .peek(System.out::print)
              .count();
    }
}

Ответ 1

Я предполагаю, что вы используете это под Java 9? Вы не изменяете свойство SIZED потока, поэтому нет необходимости выполнять любую map или peek вообще.

Другими словами, все, что вам нужно, это count итоговый результат, но в то же время вы не изменяете исходный размер List каким-либо образом (например, через filter или distinct). Это оптимизация, выполняемая в потоках.

Btw, даже если вы добавите фиктивный фильтр, это покажет, что вы ожидаете:

values.stream ()
      .map(n -> n*2)
      .peek(System.out::print)
      .filter(x -> true)
      .count();

Ответ 2

Здесь некоторые релевантные цитаты из интерфейса Javadoc of Stream:

Реализация потока допускает значительную широту при оптимизации вычисления результата. Например, реализация потока свободна для выполнения операций (или целых этапов) из конвейера потока и, следовательно, элитного вызова поведенческих параметров - если это может доказать, что это не повлияет на результат вычисления. Это означает, что побочные эффекты поведенческих параметров не всегда могут выполняться и на них не следует полагаться, если не указано иное (например, операциями терминала для каждого и для EachOrdered). (Для конкретного примера такой оптимизации см. Примечание API, зарегистрированное в операции count(). Подробнее см. В разделе побочных эффектов документации пакета потока.)

И, более конкретно, из метода Javadoc count():

Примечание API:

Реализация может решить не выполнять потоковый конвейер (последовательно или параллельно), если он способен вычислять счет непосредственно из источника потока. В таких случаях никакие исходные элементы не пройдут, и никакие промежуточные операции не будут оценены. Поведенческие параметры с побочными эффектами, которые сильно обескуражены, за исключением безобидных случаев, таких как отладка, могут быть затронуты. Например, рассмотрим следующий поток:

List<String> l = Arrays.asList("A", "B", "C", "D");
long count = l.stream().peek(System.out::println).count();

Количество элементов, охватываемых источником потока, List, известно и промежуточная операция, peek, не вводит или не удаляет элементы из потока (как это может быть в случае операций flatMap или фильтра). Таким образом, счетчик представляет собой размер списка, и нет необходимости выполнять конвейер и, в качестве побочного эффекта, распечатывать элементы списка.

Эти кавычки появляются только на Javadoc Java 9, поэтому это должна быть новая оптимизация.