Cómo peek () y allMatch () funcionan juntos en Java 8 Stream API

10

Encontré un cuestionario sobre Java 8 Stream API del método peek como se muestra a continuación

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

La salida es

Fred
Jim

¿Estoy confundido sobre cómo funciona esta transmisión? Mi resultado esperado debería ser

Fred
Jim
Sheila

El método peek () es una operación intermedia y procesa cada elemento en Stream. ¿Alguien puede explicarme esto?

Barcelona
fuente

Respuestas:

10

Es una optimización de flujo conocida como cortocircuito. Esencialmente, lo que sucede es que allMatchimpide la ejecución de operaciones intermedias innecesarias en la secuencia, porque no tiene sentido realizarlas cuando se conoce el resultado final.

Es como si esto sucediera:

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

Cuando "Jim".startsWith("F")se evalúa, el resultado de allMatch(s -> s.startsWith("F"))es conocido con certeza. No importa qué valores vienen en la tubería después "Jim", sabemos que todos los valores comienzan con "F" es falso

Esto no es específico de la combinación peek/ allMatch, hay múltiples operaciones de cortocircuito intermedio y terminal. java.util.streamlos documentos del paquete indican:

Además, algunas operaciones se consideran operaciones de cortocircuito. Una operación intermedia está en cortocircuito si, cuando se presenta con entrada infinita, puede producir una corriente finita como resultado. Una operación de terminal está en cortocircuito si, cuando se presenta con una entrada infinita, puede terminar en un tiempo finito. Tener una operación de cortocircuito en la tubería es una condición necesaria, pero no suficiente, para que el procesamiento de una corriente infinita termine normalmente en un tiempo finito.

Extienda esto a flujos finitos, y las operaciones de cortocircuito evitan la ejecución de pasos innecesarios de canalización, como en el caso de su ejemplo.

ernest_k
fuente
5
Arrays.asList("Fred", "Jim", "Sheila")
      .stream()
      .peek(System.out::println)
      .allMatch(s -> s.startsWith("F"));
  • Primera vez, Fredse imprime. Coincide así
  • Segunda vez, Jimse imprime. No coincide, por lo que allMatch termina porque "No todos coincidieron"
  • Por lo tanto, el último elemento no se consumió de la secuencia.
WJS
fuente
3

Los documentos para el peekmétodo dicen (énfasis mío):

Devuelve una secuencia que consta de los elementos de esta secuencia, además de realizar la acción proporcionada en cada elemento a medida que los elementos se consumen de la secuencia resultante .

Entonces, en este caso, peekno ve "Sheila"porque ese valor no se consume de la secuencia. Tan pronto como "Jim"se consumió, .allMatch(s -> s.startsWith("F"))ya se sabe que el resultado es false, por lo que no hay necesidad de consumir más elementos de la transmisión.

kaya3
fuente
1

Según Java Doc Of allMatch ():

Devuelve si todos los elementos de esta secuencia coinciden con el predicado proporcionado. Puede no evaluar el predicado en todos los elementos si no es necesario para determinar el resultado. Si la secuencia está vacía, se devuelve {@code true} y no se evalúa el predicado.

@apiNote

Este método evalúa la cuantificación universal del predicado sobre los elementos de la secuencia (para todos los x P (x)). Si la secuencia está vacía, se dice que la cuantificación se satisface por vacío y siempre es {@code true} (independientemente de P (x)).

predicado para aplicar a elementos de esta secuencia @return {@code true} si todos los elementos de la secuencia coinciden con el predicado proporcionado o la secuencia está vacía, de lo contrario {@code false}

En tu caso:

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

No se realizarán más evaluaciones, porque XP (X) = falso

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

Salida es:

Fred
Finda
Fish
Result true

Aquí la secuencia se procesó completamente porque xP (x) = verdadero de cada elemento

Sandeep Tiwari
fuente