Mehrere Nullprüfungen in Java 8

Lesezeit: 7 Minuten

Benutzer-Avatar
Funke

Ich habe den folgenden Code, der für mehrere Nullprüfungen etwas hässlich ist.

String s = null;

if (str1 != null) {
    s = str1;
} else if (str2 != null) {
    s = str2;
} else if (str3 != null) {
    s = str3;
} else {
    s = str4;
}

Also versuchte ich es mit Optional.ofNullable wie unten, aber es ist immer noch schwer zu verstehen, wenn jemand meinen Code liest. Was ist der beste Ansatz dafür in Java 8.

String s = Optional.ofNullable(str1)
                   .orElse(Optional.ofNullable(str2)
                                   .orElse(Optional.ofNullable(str3)
                                                   .orElse(str4)));

In Java 9 können wir verwenden Optional.ofNullablemit ORAber gibt es in Java8 einen anderen Ansatz ?

  • Java9 or Syntax String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4); sieht nicht so gut aus wie die Stream.of Ich würde ja.

    – Namann

    21. Februar 2019 um 6:55 Uhr

  • Ich weiß, dass der Benutzer nach einer Java-8-spezifischen Lösung fragt, aber allgemein würde ich mitmachen StringUtils.firstNonBlank()

    – Mohamed Anees A

    21. Februar 2019 um 12:09 Uhr

  • Das Problem ist, dass Java 8/streams dafür nicht die beste Lösung ist. Dieser Code riecht wirklich so, als wäre ein Refactor in Ordnung, aber ohne mehr Kontext ist es wirklich schwer zu sagen. Für den Anfang – warum sind drei Objekte, die wahrscheinlich so eng miteinander verwandt sind, nicht bereits in einer Sammlung?

    – Bill K

    21. Februar 2019 um 17:49 Uhr


  • @MohamedAneesA hätte die beste Antwort (als Kommentar) gegeben, aber in diesem Fall nicht die Quelle von StringUtils angegeben. Wenn Sie diese auf jeden Fall als eine Reihe separater Zeichenfolgen haben MÜSSEN, ist die Codierung als Varg-Methode wie “firstNonBlank” ideal. Die Syntax darin ist ein Array, das eine einfache For-Each-Schleife mit einer Rückgabe beim Auffinden von a erstellt Nicht-Null-Wert trivial und offensichtlich. In diesem Fall sind Java-8-Streams ein attraktives Ärgernis. Sie verleiten Sie dazu, etwas zu inlinen und zu komplizieren, das eine einfache Methode/Schleife sein sollte.

    – Bill K

    21. Februar 2019 um 17:55 Uhr


  • stackoverflow.com/q/2768054/31782

    – n0rd

    21. Februar 2019 um 20:48 Uhr

Benutzer-Avatar
Ravindra Ranwala

Du kannst es so machen:

String s = Stream.of(str1, str2, str3)
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(str4);

  • Dies. Denken Sie an das, was Sie brauchen, nicht an das, was Sie haben.

    – Thorbjørn Ravn Andersen

    21. Februar 2019 um 8:23 Uhr

  • Wie hoch ist der Geschwindigkeits-Overhead? Erstellen von Stream-Objekten, Aufrufen von 4 Methoden, Erstellen von temporären Arrays ({str1, str2, str3}) viel langsamer aussehen als ein Einheimischer if oder ?:, die die Java-Laufzeit optimieren kann. Gibt es eine Stream-spezifische Optimierung in javac und die Java-Laufzeit, die es so schnell macht wie ?:? Wenn nicht, empfehle ich diese Lösung nicht in leistungskritischem Code.

    – Pkt

    21. Februar 2019 um 9:58 Uhr


  • @pts das ist sehr wahrscheinlich langsamer als ?: Code und ich bin sicher, dass Sie es in leistungskritischem Code vermeiden sollten. Es ist jedoch viel besser lesbar und Sie sollten es meiner Meinung nach in nicht leistungskritischem Code empfehlen, der meiner Meinung nach mehr als 99 % des Codes ausmacht.

    – Aaron

    21. Februar 2019 um 10:39 Uhr

  • @pts Es gibt keine Stream-spezifischen Optimierungen, weder in javac auch nicht zur Laufzeit. Dies schließt die allgemeinen Optimierungen nicht aus, wie z. B. das Inlining des gesamten Codes, gefolgt von der Eliminierung redundanter Operationen. Im Prinzip könnte das Endergebnis so effizient sein wie die einfachen bedingten Ausdrücke, aber es ist eher unwahrscheinlich, dass es jemals dorthin kommt, da die Laufzeit den erforderlichen Aufwand nur für die heißesten Codepfade aufwenden würde.

    – Holger

    21. Februar 2019 um 10:40 Uhr


  • Tolle Lösung! Um die Lesbarkeit ein wenig zu erhöhen, würde ich vorschlagen, hinzuzufügen str4 zu den Parametern von Stream.of(...) und verwenden orElse(null) schlussendlich.

    – Danielp

    21. Februar 2019 um 23:12 Uhr

Benutzer-Avatar
Eran

Wie wäre es mit einem ternären Bedingungsoperator?

String s = 
    str1 != null ? str1 : 
    str2 != null ? str2 : 
    str3 != null ? str3 : str4
;

  • Eigentlich sucht er nach “dem besten Ansatz, dies in Java8 zu tun”. Dieser Ansatz kann in Java8 verwendet werden, also hängt alles nur davon ab, was das OP unter “das Beste” versteht und auf welchen Gründen es die Entscheidung darüber stützt, was besser ist.

    – Stultuske

    21. Februar 2019 um 7:13 Uhr

  • Normalerweise mag ich verschachtelte Ternäre nicht, aber das sieht ziemlich sauber aus.

    – JollyJoker

    21. Februar 2019 um 8:59 Uhr

  • Dies ist ein großer Schmerz für jeden, der nicht jeden Tag verschachtelte ternäre Operatoren liest.

    – Kubisch

    21. Februar 2019 um 15:46 Uhr


  • @Cubic: Wenn Sie denken, dass es schwer zu analysieren ist, schreiben Sie einen Kommentar wie // take first non-null of str1..3. Sobald Sie wissen, was es tut, wird es leicht zu sehen, wie.

    – Peter Cordes

    22. Februar 2019 um 3:36 Uhr


  • @Cubic Doch das ist ein einfach wiederholtes Muster. Sobald Sie Zeile 2 geparst haben, haben Sie das ganze Ding geparst, egal wie viele Fälle enthalten sind. Und wenn Sie fertig sind, haben Sie gelernt, eine ähnliche Auswahl von zehn verschiedenen Fällen auf eine kurze und relativ einfache Weise auszudrücken. Das nächste Mal sehen Sie a ?: Leiter, Sie werden wissen, was es tut. (Übrigens, funktionale Programmierer kennen dies als (cond ...) Klauseln: Es ist immer ein Wächter, gefolgt von dem entsprechenden Wert, der verwendet werden soll, wenn der Wächter wahr ist.)

    – Cmaster – Wiedereinsetzung von Monica

    22. Februar 2019 um 17:18 Uhr

Sie können auch eine Schleife verwenden:

String[] strings = {str1, str2, str3, str4};
for(String str : strings) {
    s = str;
    if(s != null) break;
}

Benutzer-Avatar
Walen

Aktuelle Antworten sind nett, aber Sie sollten das wirklich in eine Utility-Methode stecken:

public static Optional<String> firstNonNull(String... strings) {
    return Arrays.stream(strings)
            .filter(Objects::nonNull)
            .findFirst();
}

Diese Methode war in meinem Util Klasse seit Jahren, macht Code viel sauberer:

String s = firstNonNull(str1, str2, str3).orElse(str4);

Sie können es sogar generisch machen:

@SafeVarargs
public static <T> Optional<T> firstNonNull(T... objects) {
    return Arrays.stream(objects)
            .filter(Objects::nonNull)
            .findFirst();
}

// Use
Student student = firstNonNull(student1, student2, student3).orElseGet(Student::new);

Ich benutze eine Hilfsfunktion, so etwas wie

T firstNonNull<T>(T v0, T... vs) {
  if(v0 != null)
    return v0;
  for(T x : vs) {
    if (x != null) 
      return x;
  }
  return null;
}

Dann kann diese Art von Code geschrieben werden als

String s = firstNonNull(str1, str2, str3, str4);

  • Warum extra v0 Parameter?

    – tobias_k

    23. Februar 2019 um 10:08 Uhr

  • @tobias_k Der zusätzliche Parameter bei der Verwendung von varargs ist der idiomatische Weg in Java, 1 oder mehr Argumente anstelle von 0 oder mehr zu erfordern. (Siehe Punkt 53 von Effective Java, Ed. 3., der verwendet min als Beispiel.) Ich bin weniger davon überzeugt, dass dies hier angebracht ist.

    – Nick

    23. Februar 2019 um 15:49 Uhr


  • @Nick Ja, das habe ich mir schon gedacht, aber die Funktion würde ohne sie genauso gut funktionieren (tatsächlich genau gleich verhalten).

    – tobias_k

    23. Februar 2019 um 16:54 Uhr

  • Einer der Hauptgründe für die Übergabe eines zusätzlichen ersten Arguments besteht darin, das Verhalten bei der Übergabe eines Arrays explizit anzugeben. In diesem Fall möchte ich firstNonNull(arr) um arr zurückzugeben, wenn es nicht null ist. Wenn es war firstNonNull(T... vs) es würde stattdessen den ersten Nicht-Null-Eintrag in arr zurückgeben.

    – Michael Anderson

    24. Februar 2019 um 12:09 Uhr

Benutzer-Avatar
Azro

Eine Lösung, die auf beliebig viele Elemente angewendet werden kann, kann sein:

Stream.of(str1, str2, str3, str4)
      .filter(Object::nonNull)
      .findFirst()
      .orElseThrow(IllegalArgumentException::new)

Sie könnten sich eine Lösung wie unten vorstellen, aber die erste stellt sicher non nullity für alle Elemente

Stream.of(str1, str2, str3).....orElse(str4)

  • Warum extra v0 Parameter?

    – tobias_k

    23. Februar 2019 um 10:08 Uhr

  • @tobias_k Der zusätzliche Parameter bei der Verwendung von varargs ist der idiomatische Weg in Java, 1 oder mehr Argumente anstelle von 0 oder mehr zu erfordern. (Siehe Punkt 53 von Effective Java, Ed. 3., der verwendet min als Beispiel.) Ich bin weniger davon überzeugt, dass dies hier angebracht ist.

    – Nik

    23. Februar 2019 um 15:49 Uhr


  • @Nick Ja, das habe ich mir schon gedacht, aber die Funktion würde ohne sie genauso gut funktionieren (tatsächlich genau gleich verhalten).

    – tobias_k

    23. Februar 2019 um 16:54 Uhr

  • Einer der Hauptgründe für die Übergabe eines zusätzlichen ersten Arguments besteht darin, das Verhalten bei der Übergabe eines Arrays explizit anzugeben. In diesem Fall möchte ich firstNonNull(arr) um arr zurückzugeben, wenn es nicht null ist. Wenn es war firstNonNull(T... vs) es würde stattdessen den ersten Nicht-Null-Eintrag in arr zurückgeben.

    – Michael Anderson

    24. Februar 2019 um 12:09 Uhr

Sie können auch alle Strings in einem String-Array zusammenfassen und dann eine for-Schleife ausführen, um die Schleife zu überprüfen und zu unterbrechen, sobald sie zugewiesen wurde. Angenommen, s1, s2, s3, s4 sind alle Strings.

String[] arrayOfStrings = {s1, s2, s3};


s = s4;

for (String value : arrayOfStrings) {
    if (value != null) { 
        s = value;
        break;
    }
}

Bearbeitet, um die Bedingung für die Standardeinstellung auf s4 zu setzen, wenn keine zugewiesen ist.

  • Du hast ausgelassen s4 (oder str4), dem zuzuordnen ist s am Ende, auch wenn es null ist.

    – Anzeigename

    22. Februar 2019 um 16:36 Uhr

1298060cookie-checkMehrere Nullprüfungen in Java 8

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy