Warum gibt Stream.allMatch () für einen leeren Stream true zurück?


84

Mein Kollege und ich hatten einen Fehler, der auf unserer Annahme beruhte, dass ein leerer Stream-Aufruf zurückkehren allMatch()würde false.

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

Natürlich ist es unsere Schuld, Dokumentation anzunehmen und nicht zu lesen. Was ich jedoch nicht verstehe, ist, warum das Standardverhalten allMatch()für einen leeren Stream zurückgegeben wird true. Was war der Grund dafür? Wie die anyMatch()(die im Gegensatz dazu false zurückgibt) wird diese Operation auf eine zwingende Weise verwendet, die von der Monade abweicht und wahrscheinlich in einer ifAnweisung verwendet wird. Gibt es angesichts dieser Tatsachen einen Grund, warum es für die meisten Verwendungszwecke wünschenswert ist , allMatch()standardmäßig trueeinen leeren Stream zu verwenden?


4
Das ist ein bisschen komisch. Wir würden erwarten, dass wenn dies allMatchwahr ist, dies auch der Fall sein sollte anyMatch. Zusätzlich für den leeren Fall, allMatch(...) == noneMatch(...)der auch komisch ist.
Radiodef


Nur eine i -> i.isValid()Foo::isValidFoo
kurze Bemerkung

2
"Diese Operation wird in einer zwingenden Weise verwendet, die die Monade verlässt" - ich bezweifle, dass dies bei Entscheidungen eine Rolle spielt.
user253751

Antworten:


115

Dies ist als leere Wahrheit bekannt . Alle Mitglieder einer leeren Sammlung erfüllen Ihre Bedingung; Können Sie auf einen zeigen, der dies nicht tut?

In ähnlicher Weise wird anyMatchzurückgegeben false, da Sie kein Element Ihrer Sammlung finden können, das der Bedingung entspricht. Dies ist für viele Menschen verwirrend, stellt sich jedoch als die nützlichste und konsistenteste Methode heraus, um "any" und "all" für leere Mengen zu definieren.


3
Meine Güte, ich hasse boolesche Logik. Ich glaube, ich habe eine Vorstellung davon, was Sie sagen. Das Fehlen von Negativen ist positiv, aber das Fehlen von Positiven ist nicht negativ.
tmn

13
@ ThomasN. Auf die gleiche Weise ist der Wert, den das Produkt eines leeren Satzes von Zahlen ist, 1während die Summe eines leeren Satzes von Zahlen ist 0. Sie sind die neutralen Elemente für die Multiplikation / Addition. Im Fall von Booleschen Werten haben Sie das True and x = xund False or x = xwenn Sie also verallgemeinern andund orSequenzen (das ist was allund anysind) verallgemeinern , erhalten Sie Trueund Falsefür den leeren Fall, dh sie sind jeweils neutrale Elemente.
Bakuriu

9
@ ThomasN. anyMatchTests für das Fehlen von Positiven, allMatchTests für das Fehlen von Negativen.
user253751

3
Beachten Sie, dass Sie auf diese Weise nette Dinge wie das Gesetz von De Morgan tun können: stream.allMatch (Prädikat) ist dasselbe wie! Stream.anyMatch (Prädikat.negate ()). In ähnlicher Weise ist! Stream.allMatch (predicate.negate ()) dasselbe wie stream.anyMatch (predicate).
Hans

1
@PatrickBard: Sie können sagen, "können Sie auf eine zeigen, die dies tut", und das ist vollständig gültig , aber was es bedeutet, ist, dass alle Mitglieder der Sammlung die Bedingung nicht erfüllen. Alle Mitglieder der Sammlung erfüllen die Bedingung, und alle Mitglieder der Sammlung erfüllen die Bedingung nicht. Dies sind andere Aussagen als "Es ist falsch, dass alle Mitglieder der Sammlung die Bedingung erfüllen". Wie ich schon sagte, es ist verwirrend.
user2357112 unterstützt Monica

10

Hier ist eine andere Möglichkeit, darüber nachzudenken:

allMatch()ist zu &&was sum()ist zu+

Betrachten Sie die folgenden logischen Aussagen:

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

Dies ist sinnvoll, da sum()es sich nur um eine Verallgemeinerung von handelt +. Was passiert jedoch, wenn Sie ein weiteres Element entfernen?

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

Wir können sehen, dass es sinnvoll ist IntStream.of().sum(), die Summe einer leeren Folge von Zahlen auf eine bestimmte Weise zu definieren. Dies gibt uns das "Identitätselement" der Summierung oder den Wert, der, wenn er zu etwas hinzugefügt wird, keine Wirkung hat ( 0).

Wir können dieselbe Logik auf die BooleanAlgebra anwenden .

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

Allgemeiner:

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

Wenn stream = Stream.of()dann muss diese Regel noch gelten. Wir können das "Identitätselement" von && verwenden, um dies zu lösen. true && thing == thing, so Stream.of().allMatch(it -> it) == true.


6

Wenn ich anrufe list.allMatch(oder seine Analoga in anderen Sprachen), möchte ich feststellen, ob Elemente in listnicht mit dem Prädikat übereinstimmen. Wenn keine Elemente vorhanden sind, stimmt möglicherweise keines überein. Meine folgende Logik würde Elemente auswählen und erwarten, dass sie mit dem Prädikat übereinstimmen. Für eine leere Liste wähle ich keine Elemente aus und die Logik ist weiterhin korrekt.

Was passiert , wenn allMatchwieder falsefür eine leere Liste?

Meine einfache Logik würde scheitern:

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

Ich muss daran denken, den Scheck durch zu ersetzen !myList.empty() && !myList.allMatch().

Kurz gesagt, die allMatchRückkehr truezu einer leeren Liste ist nicht nur logisch sinnvoll, sondern liegt auch auf dem glücklichen Weg der Ausführung, der weniger Überprüfungen erfordert.


Sie meinten wahrscheinlichif (!allMatch)
Assylias

3

Es sieht so aus, als ob die Basis die mathematische Induktion ist. Für die Informatik könnte eine Anwendung davon ein Basisfall eines rekursiven Algorithmus sein.

Wenn der Strom leer ist, gilt die Quantifizierung als vakant erfüllt und ist immer wahr. Oracle Docs: Stream-Vorgänge und Pipelines

Der Schlüssel hier ist, dass es "leer zufrieden" ist, was von Natur aus etwas irreführend ist. Wikipedia hat eine anständige Diskussion darüber.

In der reinen Mathematik sind vakuumwahre Aussagen für sich genommen im Allgemeinen nicht von Interesse, aber sie entstehen häufig als Basisfall für Beweise durch mathematische Induktion. Wikipedia: Leere Wahrheit


1

Während diese Frage bereits mehrfach richtig beantwortet wurde, möchte ich einen mathematischeren Ansatz einbringen.

Dafür möchte ich den Stream als Set betrachten (im mathematischen Sinne). Dann

emptyStream.allMatch(x-> p(x))

entspricht Geben Sie hier die Bildbeschreibung einwhile

emtpyStream.anyMatch(x -> p(x))

entspricht Geben Sie hier die Bildbeschreibung ein.

Dass der zweite Teil falsch ist, liegt auf der Hand, da die leere Menge keine Elemente enthält. Der erste ist etwas kniffliger. Sie können entweder akzeptieren, dass es per Definition wahr ist, oder die anderen Antworten aus einigen Gründen prüfen, warum dies so sein sollte.

Ein Beispiel zur Veranschaulichung dieses Unterschieds sind Aussagen wie "Alle auf dem Mars lebenden Menschen haben 3 Beine" (wahr) und "Auf dem Mars lebt ein Mensch mit 3 Beinen" (falsch)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.