anymatch allmatch java lambda java-8 java-stream

java - anymatch - ¿Por qué Stream.allMatch() devuelve verdadero para una secuencia vacía?



match java 8 (4)

Mi colega y yo tuvimos un error que se debió a nuestra suposición de que una secuencia vacía que llama a allMatch() devolvería false .

if (myItems.allMatch(i -> i.isValid()) { //do something }

Por supuesto, es nuestra culpa asumir y no leer la documentación. Pero lo que no entiendo es por qué el allMatch() predeterminado de allMatch() para una secuencia vacía devuelve true . ¿Cuál fue el razonamiento para esto? Al igual que anyMatch() (que por el contrario devuelve falso), esta operación se usa de una manera imperativa que abandona la mónada y probablemente se usa en una declaración if . Teniendo en cuenta esos hechos, ¿hay alguna razón por la que es allMatch() que allMatch() true en una secuencia vacía para la mayoría de los usos?


Aquí hay otra forma de pensar sobre esto:

allMatch() es a && qué sum() es a +

Considere las siguientes declaraciones lógicas:

IntStream.of(1, 2).sum() + 3 == IntStream.of(1, 2, 3).sum() IntStream.of(1).sum() + 2 == IntStream.of(1, 2).sum()

Esto tiene sentido porque sum() es solo una generalización de + . Sin embargo, ¿qué sucede cuando eliminas un elemento más?

IntStream.of().sum() + 1 == IntStream.of(1).sum()

Podemos ver que tiene sentido definir IntStream.of().sum() , o la suma de una secuencia vacía de números, de una manera particular. Esto nos da el "elemento de identidad" de la suma, o el valor que, cuando se agrega a algo, no tiene ningún efecto ( 0 ).

Podemos aplicar la misma lógica al álgebra Boolean .

Stream.of(true, true).allMatch(it -> it) == Stream.of(true).allMatch(it -> it) && true

Más genéricamente:

stream.concat(Stream.of(thing)).allMatch(it -> it) == stream.allMatch(it -> it) && thing

Si stream = Stream.of() , esta regla aún debe aplicarse. Podemos usar el "elemento de identidad" de && para resolver esto. true && thing == thing , entonces Stream.of().allMatch(it -> it) == true .


Cuando llamo list.allMatch (o sus análogos en otros idiomas), quiero detectar si alguno de los elementos de la list no coincide con el predicado. Si no hay elementos, ninguno podría fallar. Mi siguiente lógica elegiría elementos y esperaría que coincidieran con el predicado. Para una lista vacía, no elegiré ningún elemento y la lógica seguirá siendo sólida.

¿Qué pasa si allMatch devuelve false para una lista vacía?

Mi lógica directa fallaría:

if (!myList.allMatch(predicate)) { throw new InvalidDataException("Some of the items failed to match!"); } for (Item item : myList) { ... }

!myList.empty() && !myList.allMatch() recordar reemplazar el cheque con !myList.empty() && !myList.allMatch() .

En resumen, allMatch devuelve true para una lista vacía no solo es lógicamente sólido, sino que también se encuentra en el camino feliz de la ejecución, que requiere menos comprobaciones.


Esto se conoce como verdad vacía . Todos los miembros de una colección vacía satisfacen su condición; después de todo, ¿puedes señalar uno que no lo haga?

Del mismo modo, anyMatch devuelve false , porque no puede encontrar un elemento de su colección que coincida con la condición. Esto es confuso para muchas personas, pero resulta ser la forma más útil y consistente de definir "cualquiera" y "todos" para conjuntos vacíos.


Parece que la base de esto es la inducción matemática. Para la informática, una aplicación de esto podría ser un caso base de un algoritmo recursivo.

Si la corriente está vacía, se dice que la cuantificación se satisface por vacío y siempre es cierta. Documentos de Oracle: transmisión de operaciones y canalizaciones

La clave aquí es que está "satisfecho por vacío", lo que, por naturaleza, es algo engañoso. Wikipedia tiene una discusión decente al respecto.

En matemática pura, las declaraciones verdaderas vacías generalmente no son de interés en sí mismas, pero con frecuencia surgen como el caso base de las pruebas por inducción matemática. Wikipedia: La verdad vacía