479 Stimmen

Wie man ein Methoden-Referenz-Predicate negiert

In Java 8 kannst du eine Methodenreferenz verwenden, um einen Stream zu filtern, zum Beispiel:

Stream s = ...;
long emptyStrings = s.filter(String::isEmpty).count();

Gibt es eine Möglichkeit, eine Methodenreferenz zu erstellen, die die Negation einer vorhandenen ist, also so etwas wie:

long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

Ich könnte die not Methode wie unten erstellen, aber ich frage mich, ob die JDK etwas Ähnliches anbietet.

static  Predicate not(Predicate p) { return o -> !p.test(o); }

425voto

Anton Balaniuc Punkte 9619

Predicate.not( … )

java-11 bietet eine neue Methode Predicate#not

So können Sie die Methodenreferenz negieren:

Stream s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();

249voto

davidillsley Punkte 2580

Ich plane, das folgende statisch zu importieren, um die Verwendung des Methodenverweises inline zu ermöglichen:

public static  Predicate not(Predicate t) {
    return t.negate();
}

z.B.

Stream s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

Update: Ab Java-11 bietet die JDK auch eine ähnliche Lösung integriert an.

163voto

The Coordinator Punkte 12577

Es gibt eine Möglichkeit, einen Methodenverweis zu komponieren, der das Gegenteil eines aktuellen Methodenverweises ist. Siehe die Antwort von @vlasec unten, die zeigt, wie man den Methodenverweis explizit in ein Predicate umwandelt und ihn dann mit der Funktion negate konvertiert. Das ist eine Möglichkeit unter ein paar anderen nicht allzu mühsamen Möglichkeiten, es zu tun.

Das Gegenteil davon:

Stream s = ...;
int leereStrings = s.filter(String::isEmpty).count();

ist dies:

Stream s = ...;
int nichtLeereStrings = s.filter(((Predicate) String::isEmpty).negate()).count()

oder dies:

Stream s = ...;
int nichtLeereStrings = s.filter( it -> !it.isEmpty() ).count();

Persönlich ziehe ich die letzte Technik vor, weil ich es klarer finde, it -> !it.isEmpty() zu lesen als einen langen, ausführlichen expliziten Cast und dann Negation.

Man könnte auch ein Prädikat erstellen und wiederverwenden:

Predicate nichtLeer = (String it) -> !it.isEmpty();

Stream s = ...;
int nichtLeereStrings = s.filter(nichtLeer).count();

Oder, wenn man eine Sammlung oder ein Array hat, einfach eine for-Schleife verwenden, die einfach ist, weniger Overhead hat und möglicherweise schneller ist:

int nichtLeer = 0;
for(String s : list) if(!s.isEmpty()) nichtLeer++;

*Wenn Sie wissen möchten, was schneller ist, verwenden Sie JMH http://openjdk.java.net/projects/code-tools/jmh und vermeiden Sie handgeschriebenen Benchmark-Code, es sei denn, er vermeidet alle JVM-Optimierungen — siehe Java 8: Leistung von Streams vs. Collections

**Ich bekomme Kritik dafür, dass ich die Technik der for-Schleife schneller finde. Sie eliminiert die Erstellung eines Streams, sie eliminiert die Verwendung eines weiteren Methodenaufrufs (negatives Funktion für Prädikat), und sie eliminiert eine temporäre Akkumulatorliste/-zähler. Also ein paar Dinge, die durch das letzte Konstrukt gespart werden, könnten es schneller machen.

Ich finde es jedoch einfacher und schöner, auch wenn es nicht schneller ist. Wenn die Aufgabe einen Hammer und einen Nagel erfordert, bringen Sie keine Kettensäge und Kleber mit! Ich weiß, dass einige von Ihnen damit ein Problem haben.

Wunschliste: Ich würde gerne sehen, dass die Java Stream-Funktionen sich ein wenig weiterentwickeln, jetzt da Java-Anwender vertrauter mit ihnen sind. Zum Beispiel könnte die Methode 'count' in Stream ein Predicate akzeptieren, so dass dies direkt gemacht werden kann, wie folgt:

Stream s = ...;
int nichtLeereStrings = s.count(it -> !it.isEmpty());

oder

List list = ...;
int nichtLeereStrings = list.count(it -> !it.isEmpty());

98voto

Vlasec Punkte 5291

Predicate hat die Methoden and, or und negate.

Allerdings ist String::isEmpty kein Predicate, sondern einfach ein String -> Boolean Lambda und es könnte immer noch alles werden, z.B. Function. Typableitung muss zuerst geschehen. Die filter Methode leitet den Typ implizit ab. Aber wenn du es negierst bevor du es als Argument übergibst, passiert das nicht mehr. Wie @axtavt erwähnt hat, kann explizite Ableitung als eine hässliche Methode verwendet werden:

s.filter(((Predicate) String::isEmpty).negate()).count()

Es gibt andere Wege, die in anderen Antworten empfohlen werden, wobei die Verwendung der statischen not Methode und des Lambdas wahrscheinlich die besten Ideen sind. Dies schließt den tl;dr Abschnitt ab.


Allerdings, wenn du ein tieferes Verständnis der Lambda-Typableitung möchtest, würde ich es gerne etwas genauer erklären, indem ich Beispiele verwende. Schau dir diese an und versuche herauszufinden, was passiert:

Object obj1                  = String::isEmpty;
Predicate p1         = s -> s.isEmpty();
Function f1 = String::isEmpty;
Object obj2                  = p1;
Function f2 = (Function) obj2;
Function f3 = p1::test;
Predicate p2        = s -> s.isEmpty();
Predicate p3        = String::isEmpty;
  • obj1 kompiliert nicht - Lambdas müssen ein funktionales Interface ableiten (= mit einer abstrakten Methode)
  • p1 und f1 funktionieren einwandfrei, sie leiten jeweils einen anderen Typ ab
  • obj2 castet ein Predicate zu Object - albern aber gültig
  • f2 scheitert zur Laufzeit - du kannst ein Predicate nicht zu einer Function casten, es geht nicht mehr um Ableitung
  • f3 funktioniert - du rufst die Methode test des Predicates auf, die durch sein Lambda definiert ist
  • p2 kompiliert nicht - Integer hat keine isEmpty Methode
  • p3 kompiliert auch nicht - es gibt keine String::isEmpty statische Methode mit einem Integer Argument

52voto

Jose Alban Punkte 6403

Aufbauend auf den Antworten anderer und persönlichen Erfahrungen:

Predicate leer = String::isEmpty;
inhalt.stream()
       .filter(leer.negate())

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X