Как peek () и allMatch () работают вместе в Java 8 Stream API

10

Я нашел тест о Java 8 Stream API метода просмотра, как показано ниже

Arrays.asList("Fred", "Jim", "Sheila")
      .stream()
      .peek(System.out::println)
      .allMatch(s -> s.startsWith("F"));

Выход

Fred
Jim

Я запутался, как работает этот поток? Мой ожидаемый результат должен быть

Fred
Jim
Sheila

Метод peek () является промежуточной операцией и обрабатывает каждый элемент в Stream. Может кто-нибудь объяснить мне это.

Барселона
источник

Ответы:

10

Это оптимизация потока, известная как короткое замыкание. По сути, происходит то, что allMatchпредотвращает выполнение ненужных промежуточных операций в потоке, поскольку нет смысла их выполнять, когда известен конечный результат.

Как будто это случилось:

take"Fred"
peek("Fred")
evaluate("Fred".startsWith("F"))
decide whether the result of allMatch() is known for sure: Not yet

take"Jim"
peek("Jim")
evaluate("Jim".startsWith("F"))
decide whether the result of allMatch() is known for sure: Yes

Когда "Jim".startsWith("F")оценивается, результат allMatch(s -> s.startsWith("F"))известен наверняка. Неважно, какие значения поступают в конвейер после "Jim", мы знаем, что все значения, начинающиеся с «F», ложны

Это не относится к комбинации peek/ allMatch, есть несколько промежуточных и терминальных операций короткого замыкания. Состояние java.util.streamдокументов пакета :

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

Расширьте это до конечных потоков, и операции короткого замыкания устраняют выполнение ненужных шагов конвейера, как в случае вашего примера.

ernest_k
источник
5
Arrays.asList("Fred", "Jim", "Sheila")
      .stream()
      .peek(System.out::println)
      .allMatch(s -> s.startsWith("F"));
  • Первый раз, Fredпечатается. Это соответствует так
  • Второй раз, Jimпечатается. Это не соответствует, поэтому allMatch завершается, потому что «Все не совпадают»
  • Таким образом, последний элемент не был использован из потока.
WJS
источник
3

В документах для peekметода говорит (курсив мой):

Возвращает поток, состоящий из элементов этого потока, дополнительно выполняя предоставленное действие для каждого элемента, поскольку элементы потребляются из результирующего потока .

Так что в этом случае peekне видит, "Sheila"потому что это значение не используется из потока. Как только он "Jim"был израсходован, результат, .allMatch(s -> s.startsWith("F"))как известно, уже есть false, поэтому нет необходимости использовать больше элементов из потока.

kaya3
источник
1

Согласно Java Doc AllMatch ():

Возвращает, все ли элементы этого потока соответствуют предоставленному предикату. Не может оценить предикат по всем элементам, если это не необходимо для определения результата. Если поток пуст, то возвращается {@code true}, а предикат не оценивается.

@apiNote

Этот метод оценивает универсальное количественное определение предиката по элементам потока (для всех x P (x)). Если поток пустой, то считается, что количественное определение выполнено в вакууме и всегда {@code true} (независимо от P (x)).

предикат для применения к элементам этого потока @return {@code true}, если либо все элементы потока соответствуют предоставленному предикату, либо поток пуст, в противном случае {@code false}

В твоем случае:

1-

p(x) : s -> s.startsWith("F")

X : "Fred"

result : X P(X) = true

2-

p(x) : s -> s.startsWith("F")

X : "Jim"

result : X P(X) = false

Дальнейшая оценка не будет выполнена, потому что XP (X) = false

boolean result = Arrays.asList("Fred", "Finda", "Fish")
            .stream()
            .peek(System.out::println)
            .allMatch(s -> s.startsWith("F"));
    System.out.println("Result "+result);

Выход:

Fred
Finda
Fish
Result true

Здесь поток обрабатывается полностью, потому что xP (x) = true от каждого элемента

Сандип Тивари
источник