Come ottenere il primo valore non nullo in Java?


154

Esiste un equivalente Java della COALESCEfunzione SQL ? Cioè, c'è un modo per restituire il primo valore non nullo di più variabili?

per esempio

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

Voglio avere in qualche modo una dichiarazione che restituirà il primo valore non nullo di a, be c- in questo caso, che sarebbe tornato b, o 4.4. (Qualcosa come il metodo sql - return COALESCE(a,b,c)). So che posso farlo esplicitamente con qualcosa del tipo:

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

Ma mi chiedevo se ci fosse una funzione incorporata e accettata per raggiungere questo obiettivo.


3
Non dovresti aver bisogno di una funzione come questa in quanto non calcoleresti "c" se "b" ha la risposta che desideri. cioè non costruiresti un elenco di possibili risposte solo per conservarne una.
Peter Lawrey,

Avvertenza: non tutti i cortocircuiti RDBMS su COALESCE. Oracle solo di recente ha iniziato a farlo.
Adam Gent,

3
@ BrainSlugs83 Seriamente? Java dovrebbe?
Dmitry Ginzburg,

Risposte:


108

No, non c'è.

Il più vicino che puoi ottenere è:

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

Per motivi efficienti, è possibile gestire i casi comuni come segue:

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 ...
}

3
i motivi di efficienza che ho menzionato sopra è che un'allocazione di array avverrà ogni volta che invocherai la versione var arg del metodo. questo potrebbe essere uno spreco di oggetti a mano piena, che sospetto sarà un uso comune.
les2,

Freddo. Grazie. In tal caso probabilmente mi atterrò agli operatori condizionali nidificati in questo caso poiché è l'unica volta che deve essere utilizzato e il metodo definito dall'utente sarebbe eccessivo ...
froadie

8
Lo tirerei ancora fuori in un metodo di supporto privato piuttosto che lasciare un blocco condizionale "spaventoso" nel codice - "che cosa fa?" in questo modo, se mai dovessi riutilizzarlo, puoi utilizzare gli strumenti di refactoring nell'IDE per spostare il metodo nella classe di utilità. avere il metodo nominato aiuta a documentare l'intento del codice, che è sempre una buona cosa, IMO. (e il sovraccarico della versione non var-args è probabilmente appena misurabile.)
les2

10
Attenzione: In coalesce(a, b), se bè un'espressione complessa e anon lo è null, bviene comunque valutata. Questo non è il caso dell'operatore?: Condizionale. Vedere questa risposta .
Pang

ciò richiede che tutti gli argomenti siano precalcolati prima della chiamata alla coalizione, inutili per motivi di performance
Ivan G.


59

Se ci sono solo due variabili da controllare e stai usando Guava, puoi usare MoreObjects.firstNonNull (T prima, T seconda) .


49
Objects.firstNonNull accetta solo due argomenti; non ci sono varargs equivalenti in Guava. Inoltre, genera una NullPointerException se entrambi gli argomenti sono nulli, ciò può essere o non essere desiderabile.

2
Bel commento, Jake. Questa NullPointerException limita spesso l'utilizzo di Objects.firstNonNull. Tuttavia, l'approccio di Guava è di evitare del tutto i nulli.
Anton Shchastnyi,

4
Questo metodo è ora deprecato e l'alternativa consigliata è MoreObjects.firstNonNull
davidwebster48

1
Se NPE non è desiderato, vedere questa risposta
OrangeDog,

51

Se ci sono solo due riferimenti da testare e si sta utilizzando Java 8, è possibile utilizzare

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

Se si importa statico Opzionale, l'espressione non è troppo male.

Sfortunatamente il tuo caso con "diverse variabili" non è possibile con un metodo opzionale. Invece potresti usare:

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

23

Seguendo la risposta di LES2, è possibile eliminare alcune ripetizioni nella versione efficiente, chiamando la funzione sovraccaricata:

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);
}

5
+1 per carina. Non sono sicuro dei vantaggi in termini di efficienza rispetto al semplice ciclo, ma se hai intenzione di ottenere una piccola efficienza in questo modo, potrebbe anche essere carino.
Carl Manaster,

3
in questo modo è molto meno doloroso e meno soggetto a errori scrivere le varianti sovraccaricate!
les2,

2
Il punto della versione efficiente era di non sprecare memoria allocando un array usando varargs. Qui, stai sprecando memoria creando un frame stack per ogni coalesce()chiamata nidificata . La chiamata coalesce(a, b, c, d, e)crea fino a 3 frame stack da calcolare.
Luca,

10

Questa situazione richiede alcuni preprocessori. Perché se si scrive una funzione (metodo statico) che seleziona il primo valore non nullo, valuta tutti gli elementi. È un problema se alcuni elementi sono chiamate di metodo (potrebbero essere chiamate di metodo costose in termini di tempo). E questi metodi vengono chiamati anche se qualsiasi elemento precedente non è nullo.

Alcuni funzionano in questo modo

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

dovrebbe essere usato ma prima di compilare in codice byte dovrebbe esserci un preprocessore che trova gli usi di questa "funzione di coalescenza" e lo sostituisce con una costruzione come

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

Aggiornamento 2014-09-02:

Grazie a Java 8 e Lambdas c'è la possibilità di avere una vera fusione in Java! Inclusa la caratteristica cruciale: determinate espressioni vengono valutate solo quando necessario - se una precedente non è nulla, le seguenti non vengono valutate (i metodi non vengono chiamati, il calcolo o le operazioni su disco / rete non vengono eseguite).

Ho scritto un articolo a riguardo Java 8: coalesce - hledáme neNULLové hodnoty - (scritto in ceco, ma spero che esempi di codice siano comprensibili per tutti).


1
Bell'articolo - sarebbe bello averlo inglese, però.
quantum

1
C'è qualcosa in quella pagina del blog che non funziona con Google Translate. :-(
HairOfTheDog

5

Con Guava puoi fare:

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

che non genera NPE se entrambi ae lo bsono null.

EDIT: ho sbagliato, getta NPE. Il modo corretto come commentato da Michal Čizmazia è:

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

1
Ehi, lo fa:java.lang.NullPointerException: use Optional.orNull() instead of Optional.or(null)
Michal Čizmazia,

1
Questo è il trucco:Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull()
Michal Čizmazia,

4

Solo per completezza, il caso "diverse variabili" è davvero possibile, sebbene non sia affatto elegante. Ad esempio, per le variabili o, pe q:

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

Si prega di notare l'uso del orElseGet()caso che o, pe qnon sono variabili ma espressioni o costose o con effetti collaterali indesiderati.

Nel caso più generale 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

Questo può generare espressioni eccessivamente lunghe. Tuttavia, se stiamo cercando di spostarci in un mondo senza null, allora v[i]molto probabilmente sono già di tipo Optional<String>, al contrario di semplicemente String. In questo caso,

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

o nel caso di espressioni:

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

Inoltre, se siete anche muovendo per uno stile funzionale-dichiarativa, o, p, e qdovrebbe essere di tipo Supplier<String>simile a:

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

E poi il tutto si coalesceriduce semplicemente a o.get().

Per un esempio più concreto:

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()e ageFromInput()sarebbe già tornato Optional<Integer>, naturalmente.

E poi coalescediventa effectiveAge.get()o semplicemente effectiveAgese siamo felici con a Supplier<Integer>.

IMHO, con Java 8 vedremo sempre più codice strutturato in questo modo, poiché è estremamente autoesplicativo ed efficiente allo stesso tempo, specialmente in casi più complessi.

Mi manca una classe Lazy<T>che invoca una Supplier<T>sola volta, ma pigramente, così come la coerenza nella definizione di Optional<T>(cioè Optional<T>- Optional<T>operatori, o anche Supplier<Optional<T>>).


4

Puoi provare questo:

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

Sulla base di questa risposta


3

Che ne dici di usare i fornitori quando vuoi evitare di valutare qualche metodo costoso?

Come questo:

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

E poi usandolo in questo modo:

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

È inoltre possibile utilizzare metodi sovraccaricati per le chiamate con due, tre o quattro argomenti.

Inoltre, puoi anche usare stream con qualcosa del genere:

public static <T> T coalesce2(Supplier<T>... s) {
    return Arrays.stream(s).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null);
}

Perché avvolgere il primo argomento in a Supplierse verrà comunque ispezionato? Per motivi di uniformità?
Inego,

0

Che ne dite di:

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

Java ArrayList consente convenientemente voci nulle e questa espressione è coerente indipendentemente dal numero di oggetti da considerare. (In questo modulo, tutti gli oggetti considerati devono essere dello stesso tipo.)


-3
Object coalesce(Object... objects)
{
    for(Object o : object)
        if(o != null)
            return o;
    return null;
}

2
Dio odio i generici. Ho visto cosa significava il tuo fin dall'inizio. Ho dovuto guardare due volte @ LES2 per capire che stava facendo la stessa cosa (e probabilmente "meglio")! +1 per chiarezza
Bill K

Sì, i generici sono la strada da percorrere. Ma non ho molta familiarità con le complessità.
Eric,

10
È ora di imparare i generici :-). C'è poca differenza tra l'esempio di @ LES2 e questo, oltre a T invece di Object. -1 per la creazione di una funzione che costringerà a riportare il valore di ritorno su Double. Anche per nominare un metodo Java in maiuscolo, che può andare bene in SQL, ma non è un buon stile in Java.
Avi,

1
Mi rendo conto che tutto in maiuscolo è una cattiva pratica. Stavo solo mostrando all'OP come scrivere una funzione con il nome richiesto. D'accordo, il cast Doubleè tutt'altro che ideale. Non ero a conoscenza del fatto che alle funzioni statiche potessero essere dati parametri di tipo. Pensavo fossero solo lezioni.
Eric
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.