Wie erhalte ich den ersten Nicht-Null-Wert in Java?

153

Gibt es ein Java-Äquivalent zur SQL- COALESCEFunktion? Gibt es eine Möglichkeit, den ersten Nicht-Null-Wert mehrerer Variablen zurückzugeben?

z.B

Double a = null;
Double b = 4.4;
Double c = null;

Ich möchte irgendwie eine Erklärung haben, die den ersten Nicht-Null - Wert von wird zurückkehren a, bund c- in diesem Fall würde es zurückgeben b, oder 4.4. (So ​​etwas wie die SQL-Methode - return COALESCE(a,b,c)). Ich weiß, dass ich es explizit mit so etwas wie:

return a != null ? a : (b != null ? b : c)

Aber ich fragte mich, ob es eine eingebaute, akzeptierte Funktion gab, um dies zu erreichen.

froadie
quelle
3
Sie sollten eine solche Funktion nicht benötigen, da Sie 'c' geneal nicht berechnen würden, wenn 'b' die gewünschte Antwort hätte. Das heißt, Sie würden keine Liste möglicher Antworten erstellen, nur um eine zu behalten.
Peter Lawrey
Vorsichtsmaßnahme: Nicht alle RDBMS-Kurzschlüsse bei COALESCE. Oracle hat erst vor kurzem damit begonnen.
Adam Gent
3
@ BrainSlugs83 Ernsthaft? Java sollte?
Dmitry Ginzburg

Antworten:

108

Nein, gibt es nicht.

Der nächste, den Sie bekommen können, ist:

public static <T> T coalesce(T ...items) {
    for(T i : items) if(i != null) return i;
    return null;
}

Aus effizienten Gründen können Sie die häufigsten Fälle wie folgt behandeln:

public static <T> T coalesce(T a, T b) {
    return a == null ? b : a;
}
public static <T> T coalesce(T a, T b, T c) {
    return a != null ? a : (b != null ? b : c);
}
public static <T> T coalesce(T a, T b, T c, T d) {
    return ...
}
les2
quelle
3
Die oben erwähnten Effizienzgründe sind, dass jedes Mal, wenn Sie die var arg-Version der Methode aufrufen, eine Array-Zuordnung erfolgt. Dies könnte für Handfüllungen von Gegenständen verschwenderisch sein, von denen ich vermute, dass sie allgemein verwendet werden.
Les2
Cool. Vielen Dank. In diesem Fall werde ich mich in diesem Fall wahrscheinlich an die verschachtelten bedingten Operatoren halten, da dies das einzige Mal ist, dass sie verwendet werden müssen und die benutzerdefinierte Methode übertrieben wäre ...
froadie
8
Ich würde es immer noch in eine private Hilfsmethode ziehen, anstatt einen "beängstigend aussehenden" bedingten Block im Code zu belassen - "Was macht das?" Auf diese Weise können Sie die Methode mithilfe der Refactoring-Tools in Ihrer IDE in die Utility-Klasse verschieben, falls Sie sie jemals wieder verwenden müssen. Die benannte Methode hilft dabei, die Absicht des Codes zu dokumentieren, was immer gut ist, IMO. (und der Overhead der Nicht-Var-Args-Version ist wahrscheinlich kaum messbar.)
les2
10
Achtung: In coalesce(a, b), wenn bes sich um einen komplexen Ausdruck handelt und anicht null, bwird noch ausgewertet. Dies ist beim bedingten Operator ?: Nicht der Fall. Siehe diese Antwort .
Pang
Dies erfordert, dass jedes Argument vor dem Aufruf zur Verschmelzung vorberechnet wird, aus Leistungsgründen sinnlos
Ivan G.
59

Wenn nur zwei Variablen überprüft werden müssen und Sie Guava verwenden, können Sie MoreObjects.firstNonNull (T first, T second) verwenden .

Dave
quelle
49
Objects.firstNonNull akzeptiert nur zwei Argumente. In Guave gibt es kein Varargs-Äquivalent. Außerdem wird eine NullPointerException ausgelöst, wenn beide Argumente null sind - dies kann wünschenswert sein oder nicht.
2
Guter Kommentar, Jake. Diese NullPointerException schränkt häufig die Verwendung von Objects.firstNonNull ein. Es ist jedoch Guavas Ansatz, Nullen überhaupt zu vermeiden.
Anton Shchastnyi
4
Diese Methode ist jetzt veraltet und die empfohlene Alternative ist MoreObjects.firstNonNull
davidwebster48
1
Wenn NPE unerwünscht ist, dann sehen Sie diese Antwort
OrangeDog
51

Wenn nur zwei Referenzen getestet werden müssen und Sie Java 8 verwenden, können Sie diese verwenden

Object o = null;
Object p = "p";
Object r = Optional.ofNullable( o ).orElse( p );
System.out.println( r );   // p

Wenn Sie statisch importieren Optional ist der Ausdruck nicht schlecht.

Leider ist Ihr Fall mit "mehreren Variablen" mit einer optionalen Methode nicht möglich. Stattdessen könnten Sie verwenden:

Object o = null;
Object p = null;
Object q = "p";

Optional<Object> r = Stream.of( o, p, q ).filter( Objects::nonNull ).findFirst();
System.out.println( r.orElse(null) );   // p
Christian Ullenboom
quelle
23

In Anlehnung an die Antwort von LES2 können Sie einige Wiederholungen in der effizienten Version vermeiden, indem Sie die überladene Funktion aufrufen:

public static <T> T coalesce(T a, T b) {
    return a != null ? a : b;
}
public static <T> T coalesce(T a, T b, T c) {
    return a != null ? a : coalesce(b,c);
}
public static <T> T coalesce(T a, T b, T c, T d) {
    return a != null ? a : coalesce(b,c,d);
}
public static <T> T coalesce(T a, T b, T c, T d, T e) {
    return a != null ? a : coalesce(b,c,d,e);
}
Eric
quelle
5
+1 für hübsch. Sie sind sich nicht sicher über die Effizienzvorteile gegenüber der einfachen Schleife, aber wenn Sie auf diese Weise eine winzige Effizienz erzielen möchten, könnte dies genauso gut hübsch sein.
Carl Manaster
3
Auf diese Weise wird es viel weniger schmerzhaft und weniger fehleranfällig, die überladenen Varianten zu schreiben!
Les2
2
Der Sinn der effizienten Version bestand darin, keinen Speicher zu verschwenden, der ein Array mithilfe von zuweist varargs. Hier verschwenden Sie Speicher, indem Sie für jeden verschachtelten coalesce()Aufruf einen Stapelrahmen erstellen . Beim Aufrufen coalesce(a, b, c, d, e)werden bis zu 3 Stapelrahmen zur Berechnung erstellt.
Luke
10

Diese Situation erfordert einen Präprozessor. Wenn Sie eine Funktion (statische Methode) schreiben, die den ersten Wert ungleich Null auswählt, werden alle Elemente ausgewertet. Es ist problematisch, wenn es sich bei einigen Elementen um Methodenaufrufe handelt (dies können zeitaufwändige Methodenaufrufe sein). Und diese Methoden werden auch dann aufgerufen, wenn ein Element vor ihnen nicht null ist.

Einige funktionieren so

public static <T> T coalesce(T ...items) 

sollte verwendet werden, aber vor dem Kompilieren in Bytecode sollte es einen Präprozessor geben, der die Verwendung dieser „Koaleszenzfunktion“ findet und sie durch eine Konstruktion wie ersetzt

a != null ? a : (b != null ? b : c)

Update 02.09.2014:

Dank Java 8 und Lambdas besteht die Möglichkeit, dass Java wirklich zusammenwächst! Einschließlich der entscheidenden Funktion: Bestimmte Ausdrücke werden nur bei Bedarf ausgewertet. Wenn der frühere nicht null ist, werden die folgenden nicht ausgewertet (Methoden werden nicht aufgerufen, Berechnungen oder Festplatten- / Netzwerkoperationen werden nicht ausgeführt).

Ich habe einen Artikel darüber geschrieben Java 8: coalesce - hledáme neNULLové hodnoty - (geschrieben auf Tschechisch, aber ich hoffe, dass Codebeispiele für alle verständlich sind).

Franta
quelle
1
Schöner Artikel - es wäre allerdings schön, wenn es Englisch wäre.
Quantum
1
Diese Blog-Seite hat etwas, das mit Google Translate nicht funktioniert. :-(
HairOfTheDog
5

Mit Guava können Sie Folgendes tun:

Optional.fromNullable(a).or(b);

was NPE nicht wirft, wenn beide aund bsind null.

EDIT: Ich habe mich geirrt, es wirft NPE. Der richtige Weg, wie von Michal Čizmazia kommentiert, ist:

Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull();
Jamol
quelle
1
Hey, es tut:java.lang.NullPointerException: use Optional.orNull() instead of Optional.or(null)
Michal Čizmazia
1
Dies macht den Trick:Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull()
Michal Čizmazia
4

Nur der Vollständigkeit halber ist der Fall "mehrere Variablen" zwar möglich, aber überhaupt nicht elegant. Zum Beispiel für Variablen o, pund q:

Optional.ofNullable( o ).orElseGet(()-> Optional.ofNullable( p ).orElseGet(()-> q ) )

Bitte beachten Sie, dass Sie sich orElseGet()um den Fall kümmern omüssen p, dass qes sich nicht um Variablen handelt, sondern um Ausdrücke, die entweder teuer sind oder unerwünschte Nebenwirkungen haben.

Im allgemeinsten Fall coalesce(e[1],e[2],e[3],...,e[N])

coalesce-expression(i) ==  e[i]  when i = N
coalesce-expression(i) ==  Optional.ofNullable( e[i] ).orElseGet(()-> coalesce-expression(i+1) )  when i < N

Dies kann zu lange Ausdrücke erzeugen. Wenn wir jedoch versuchen, uns in eine Welt ohne zu bewegen null, dann v[i]sind wir höchstwahrscheinlich schon vom Typ Optional<String>, im Gegensatz zu einfach String. In diesem Fall,

result= o.orElse(p.orElse(q.get())) ;

oder im Fall von Ausdrücken:

result= o.orElseGet(()-> p.orElseGet(()-> q.get() ) ) ;

Wenn Sie darüber hinaus auch zu einem funktionellen-deklarativem Stil bewegen, o, p, und qsollte vom Typ sein , Supplier<String>wie in:

Supplier<String> q= ()-> q-expr ;
Supplier<String> p= ()-> Optional.ofNullable(p-expr).orElseGet( q ) ;
Supplier<String> o= ()-> Optional.ofNullable(o-expr).orElseGet( p ) ;

Und dann coalescereduziert sich das Ganze einfach auf o.get().

Für ein konkreteres Beispiel:

Supplier<Integer> hardcodedDefaultAge= ()-> 99 ;
Supplier<Integer> defaultAge= ()-> defaultAgeFromDatabase().orElseGet( hardcodedDefaultAge ) ;
Supplier<Integer> ageInStore= ()-> ageFromDatabase(memberId).orElseGet( defaultAge ) ;
Supplier<Integer> effectiveAge= ()-> ageFromInput().orElseGet( ageInStore ) ;

defaultAgeFromDatabase(), ageFromDatabase()Und ageFromInput()würde schon zurückkehren Optional<Integer>, natürlich.

Und dann coalescewird das effectiveAge.get()oder einfach effectiveAgewenn wir mit einem zufrieden sind Supplier<Integer>.

IMHO, mit Java 8 werden wir immer mehr Code sehen, der so strukturiert ist, da er extrem selbsterklärend und gleichzeitig effizient ist, insbesondere in komplexeren Fällen.

Ich vermisse eine Klasse Lazy<T>, die Supplier<T>nur einmal, aber träge, sowie Konsistenz in der Definition von Optional<T>(dh Optional<T>- Optional<T>Operatoren oder sogar Supplier<Optional<T>>) aufruft .

Mario Rossi
quelle
4

Sie können dies versuchen:

public static <T> T coalesce(T... t) {
    return Stream.of(t).filter(Objects::nonNull).findFirst().orElse(null);
}

Basierend auf dieser Antwort

Lucas León
quelle
3

Wie wäre es mit Lieferanten, wenn Sie die Bewertung einer teuren Methode vermeiden möchten?

So was:

public static <T> T coalesce(Supplier<T>... items) {
for (Supplier<T> item : items) {
    T value = item.get();
    if (value != null) {
        return value;
    }
    return null;
}

Und dann benutze ich es so:

Double amount = coalesce(order::firstAmount, order::secondAmount, order::thirdAmount)

Sie können auch überladene Methoden für die Aufrufe mit zwei, drei oder vier Argumenten verwenden.

Darüber hinaus können Sie auch Streams mit folgenden Elementen verwenden:

public static <T> T coalesce2(Supplier<T>... s) {
    return Arrays.stream(s).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null);
}
Triqui
quelle
Warum das erste Argument in ein Suppliereinschließen, wenn es trotzdem überprüft wird? Aus Gründen der Einheitlichkeit?
Inego
0

Wie wäre es mit:

firstNonNull = FluentIterable.from(
    Lists.newArrayList( a, b, c, ... ) )
        .firstMatch( Predicates.notNull() )
            .or( someKnownNonNullDefault );

Java ArrayList erlaubt bequemerweise Null-Einträge und dieser Ausdruck ist unabhängig von der Anzahl der zu berücksichtigenden Objekte konsistent. (In dieser Form müssen alle betrachteten Objekte vom gleichen Typ sein.)

Lonnie
quelle
-3
Object coalesce(Object... objects)
{
    for(Object o : object)
        if(o != null)
            return o;
    return null;
}
Eric
quelle
2
Gott, ich hasse Generika. Ich habe sofort gesehen, was deins bedeutet. Ich musste zweimal auf @ LES2 schauen, um herauszufinden, dass er dasselbe tat (und wahrscheinlich "besser")! +1 für Klarheit
Bill K
Ja, Generika sind der richtige Weg. Aber ich bin mit den Feinheiten nicht allzu vertraut.
Eric
10
Zeit Generika zu lernen :-). Es gibt kaum einen Unterschied zwischen dem Beispiel von @ LES2 und diesem, abgesehen von T anstelle von Object. -1 zum Erstellen einer Funktion, die das Zurücksetzen des Rückgabewerts auf Double erzwingt. Auch zum Benennen einer Java-Methode in Großbuchstaben, was in SQL zwar in Ordnung ist, in Java jedoch keinen guten Stil aufweist.
Avi
1
Mir ist klar, dass All-Caps eine schlechte Praxis sind. Ich habe dem OP nur gezeigt, wie man eine Funktion unter dem von ihnen angeforderten Namen schreibt. Einverstanden ist die Besetzung alles Doubleandere als ideal. Mir war nur nicht bewusst, dass statischen Funktionen Typparameter zugewiesen werden können. Ich dachte, es wäre nur Unterricht.
Eric