In Java 8 können Sie eine Methodenreferenz verwenden, um einen Stream zu filtern, zum Beispiel:
Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();
Gibt es eine Möglichkeit, eine Methodenreferenz zu erstellen, die die Negation einer vorhandenen ist, dh so etwas wie:
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Ich könnte die not
Methode wie unten erstellen , habe mich aber gefragt, ob das JDK etwas Ähnliches anbietet.
static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }
Predicate.not(Predicate)
Methode. Dieses Problem ist jedoch noch offen, sodass wir es frühestens in Java 12 sehen werden (falls überhaupt).Antworten:
Predicate.not( … )
Java-11bietet eine neue Methode Prädikat # nicht
So können Sie die Methodenreferenz negieren:
quelle
Ich plane, Folgendes statisch zu importieren, damit die Methodenreferenz inline verwendet werden kann:
z.B
Update : Ab Java-11 bietet das JDK auch eine ähnliche integrierte Lösung .
quelle
Es gibt eine Möglichkeit, eine Methodenreferenz zu erstellen, die das Gegenteil einer aktuellen Methodenreferenz ist. Die Antwort von @ vlasec unten zeigt, wie Sie die Methodenreferenz explizit in a
Predicate
umwandeln und sie dann mithilfe dernegate
Funktion konvertieren . Das ist ein Weg unter ein paar anderen, nicht allzu mühsamen Wegen, dies zu tun.Das Gegenteil davon:
ist das:
oder dieses:
Persönlich bevorzuge ich die spätere Technik, weil ich es klarer finde, zu lesen
it -> !it.isEmpty()
als eine lange ausführliche explizite Besetzung und dann zu negieren.Man könnte auch ein Prädikat erstellen und es wiederverwenden:
Wenn Sie über eine Sammlung oder ein Array verfügen, verwenden Sie einfach eine for-Schleife, die einfach ist, weniger Overhead hat und * möglicherweise ** schneller ist:
* Wenn Sie wissen möchten, was schneller ist, verwenden Sie JMH http://openjdk.java.net/projects/code-tools/jmh und vermeiden Sie Hand-Benchmark-Code, es sei denn, es vermeidet alle JVM-Optimierungen - siehe Java 8: Leistung von Streams vs Sammlungen
** Ich bekomme Flak, weil ich behaupte, dass die For-Loop-Technik schneller ist. Es eliminiert eine Stream-Erstellung, es eliminiert die Verwendung eines anderen Methodenaufrufs (negative Funktion für Prädikat) und es eliminiert eine temporäre Akkumulatorliste / Zähler. Also ein paar Dinge, die vom letzten Konstrukt gespeichert werden und die es möglicherweise schneller machen.
Ich denke, es ist einfacher und schöner, wenn auch nicht schneller. Wenn der Job einen Hammer und einen Nagel erfordert, bringen Sie keine Kettensäge und keinen Kleber mit! Ich weiß, dass einige von Ihnen Probleme damit haben.
Wunschliste: Ich würde gerne sehen, dass sich die Java-
Stream
Funktionen etwas weiterentwickeln, da Java-Benutzer mit ihnen besser vertraut sind. Zum Beispiel könnte die 'count'-Methode in Stream a akzeptieren,Predicate
so dass dies direkt wie folgt erfolgen kann:quelle
Predicate.negate(String::isEmpty);
ohne das umständliche Casting.Predicate
hat Methodenand
,or
undnegate
.Ist
String::isEmpty
jedoch keinPredicate
, es ist nur einString -> Boolean
Lambda und es könnte immer noch alles werden, zFunction<String, Boolean>
. Typinferenz ist das, was zuerst passieren muss. Diefilter
Methode leitet den Typ implizit ab . Wenn Sie es jedoch negieren, bevor Sie es als Argument übergeben, geschieht dies nicht mehr. Wie @axtavt erwähnte, kann explizite Folgerung als hässlicher Weg verwendet werden:In anderen Antworten werden andere Möglichkeiten empfohlen, wobei statische
not
Methode und Lambda höchstwahrscheinlich die besten Ideen sind. Dies schließt die tl; dr Abschnitt.Wenn Sie jedoch ein tieferes Verständnis der Inferenz vom Lambda-Typ wünschen, möchte ich dies anhand von Beispielen etwas ausführlicher erläutern. Schauen Sie sich diese an und versuchen Sie herauszufinden, was passiert:
Predicate
zuObject
dummes, aber gültigesPredicate
zuFunction
, es geht nicht mehr um Inferenztest
, die durch sein Lambda definiert istInteger
hat keineisEmpty
MethodeString::isEmpty
statische Methode mitInteger
ArgumentIch hoffe, dies hilft, mehr Einblick in die Funktionsweise der Typinferenz zu bekommen.
quelle
Aufbauend auf den Antworten und persönlichen Erfahrungen anderer:
quelle
::
wie gewünscht inline einfügen (String::isEmpty.negate()
), aber wenn Sie zuerst einer Variablen zuweisen (oder zuerst umwandelnPredicate<String>
), funktioniert dies. Ich denke, Lambda!
wird in den meisten Fällen am besten lesbar sein, aber es ist hilfreich zu wissen, was kompiliert werden kann und was nicht.Eine andere Möglichkeit besteht darin, Lambda-Casting in nicht mehrdeutigen Kontexten in einer Klasse zu verwenden:
... und dann statisch die Utility-Klasse importieren:
quelle
Sollte nicht das
Predicate#negate
sein, wonach Sie suchen?quelle
Predicate
erste bekommen.String::isEmpty()
zuPredicate<String>
vor - es ist sehr hässlich.Predicate<String> p = (Predicate<String>) String::isEmpty;
und verwendenp.negate()
.s -> !s.isEmpty()
in diesem Fall lieber schreiben !In diesem Fall könnten Sie das
org.apache.commons.lang3.StringUtils
und tunquelle
String::isEmpty
als Beispiel. Es sind immer noch relevante Informationen, wenn Sie diesen Anwendungsfall haben, aber wenn er nur den String-Anwendungsfall beantwortet, sollte er nicht akzeptiert werden.Ich habe eine vollständige Utility-Klasse geschrieben (inspiriert von Askars Vorschlag), die Java 8-Lambda-Ausdrücke verwenden und sie (falls zutreffend) in jedes im Paket definierte typisierte Standard-Java 8-Lambda umwandeln kann
java.util.function
. Sie können zum Beispiel Folgendes tun:asPredicate(String::isEmpty).negate()
asBiPredicate(String::equals).negate()
Da es zahlreiche Unklarheiten geben würde, wenn alle statischen Methoden nur benannt würden
as()
, habe ich mich dafür entschieden, die Methode "as" gefolgt vom zurückgegebenen Typ aufzurufen. Dies gibt uns die volle Kontrolle über die Lambda-Interpretation. Unten ist der erste Teil der (etwas großen) Utility-Klasse, der das verwendete Muster enthüllt.Schauen Sie sich hier die gesamte Klasse an (im Kern).
quelle
Sie können Prädikate aus Eclipse-Sammlungen verwenden
Wenn Sie die Zeichenfolgen nicht ändern können von
List
:Wenn Sie nur eine Negation von benötigen
String.isEmpty()
, können Sie auch verwendenStringPredicates.notEmpty()
.Hinweis: Ich bin ein Mitarbeiter von Eclipse Collections.
quelle
Sie können dies so lange erreichen
emptyStrings = s.filter(s->!s.isEmpty()).count();
quelle
Wenn Sie Spring Boot (2.0.0+) verwenden, können Sie Folgendes verwenden:
Welches tut:
return (str != null && !str.isEmpty());
Es wird also den erforderlichen Negationseffekt für haben
isEmpty
quelle