¿Cómo obtener el primer valor no nulo en Java?


154

¿Existe un equivalente Java de SQL COALESCE función ? Es decir, ¿hay alguna forma de devolver el primer valor no nulo de varias variables?

p.ej

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

Quiero tener de alguna manera una declaración que devolverá el primer valor no nulo de a, by c- en este caso, sería volver b, o 4.4. (Algo así como el método sql - return COALESCE(a,b,c)). Sé que puedo hacerlo explícitamente con algo como:

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

Pero me preguntaba si había alguna función incorporada y aceptada para lograr esto.


3
No debería necesitar una función como esta, ya que generalmente no calcularía 'c' si 'b' tiene la respuesta que desea. es decir, no crearía una lista de posibles respuestas solo para mantener una.
Peter Lawrey

Advertencia: No todos los cortocircuitos RDBMS en COALESCE. Oracle solo recientemente comenzó a hacerlo.
Adam Gent

3
@ BrainSlugs83 ¿En serio? Java debería?
Dmitry Ginzburg

Respuestas:


108

No, no hay

Lo más cercano que puede llegar es:

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

Por razones eficientes, puede manejar los casos comunes de la siguiente manera:

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
Las razones de eficiencia que mencioné anteriormente es que una asignación de matriz ocurrirá cada vez que invoque la versión var arg del método. Esto podría ser un desperdicio para un montón de artículos, lo que sospecho que será de uso común.
les2

Frio. Gracias. En ese caso, probablemente me atendré a los operadores condicionales anidados en este caso, ya que es la única vez que tiene que usarse y el método definido por el usuario sería excesivo ...
froadie

8
Todavía lo sacaría a un método auxiliar privado en lugar de dejar un bloque condicional de "aspecto aterrador" en el código: "¿qué hace eso?" de esa manera, si alguna vez necesita usarlo nuevamente, puede usar las herramientas de refactorización en su IDE para mover el método a la clase de utilidad. tener el método con nombre ayuda a documentar la intención del código, que siempre es algo bueno, IMO. (y la sobrecarga de la versión no var-args probablemente sea apenas medible).
les2

10
Cuidado: en coalesce(a, b), si bes una expresión compleja y ano lo es null, btodavía se evalúa. Este no es el caso del operador condicional? Mira esta respuesta .
Pang

esto requiere que cada argumento sea precalculado antes del llamado a unirse, sin sentido por razones de rendimiento
Ivan G.


59

Si solo hay dos variables para verificar y está usando Guava, puede usar MoreObjects.firstNonNull (T primero, T segundo) .


49
Objects.firstNonNull solo toma dos argumentos; no hay varargs equivalentes en guayaba. Además, arroja una NullPointerException si ambos argumentos son nulos, esto puede o no ser deseable.

2
Buen comentario, Jake. Esta excepción NullPointerException a menudo restringe el uso de Objects.firstNonNull. Sin embargo, el enfoque de Guava es evitar los nulos en absoluto.
Anton Shchastnyi

44
Ese método ahora está en desuso, y la alternativa recomendada es MoreObjects.firstNonNull
davidwebster48

1
Si NPE no es deseado, entonces vea esta respuesta
OrangeDog

51

Si solo hay dos referencias para probar y está utilizando Java 8, puede usar

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

Si importa estática Opcional, la expresión no es tan mala.

Lamentablemente, su caso con "varias variables" no es posible con un método opcional. En su lugar, podría usar:

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

Siguiendo con la respuesta de LES2, puede eliminar algunas repeticiones en la versión eficiente, llamando a la función sobrecargada:

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

55
+1 para guapa. No estoy seguro acerca de los beneficios de eficiencia sobre el ciclo simple, pero si va a obtener una pequeña eficiencia de esta manera, también podría ser bonito.
Carl Manaster

3
¡De esta manera, es mucho menos doloroso y menos propenso a errores escribir las variantes sobrecargadas!
les2

2
El objetivo de la versión eficiente era no desperdiciar memoria asignando una matriz mediante el uso varargs. Aquí, está desperdiciando memoria creando un marco de pila para cada coalesce()llamada anidada . Llamar coalesce(a, b, c, d, e)crea hasta 3 cuadros de pila para calcular.
Lucas el

10

Esta situación requiere algún preprocesador. Porque si escribe una función (método estático) que selecciona el primer valor no nulo, evalúa todos los elementos. Es un problema si algunos elementos son llamadas a métodos (pueden ser llamadas a métodos costosos). Y se llama a estos métodos incluso si algún elemento antes de ellos no es nulo.

Algunas funciones como esta

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

debe usarse, pero antes de compilar en el código de bytes, debe haber un preprocesador que encuentre el uso de esta "función de fusión" y la reemplace con una construcción como

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

Actualización 2014-09-02:

¡Gracias a Java 8 y Lambdas existe la posibilidad de tener una verdadera fusión en Java! Incluyendo la característica crucial: las expresiones particulares se evalúan solo cuando es necesario; si una anterior no es nula, las siguientes no se evalúan (no se llaman métodos, no se realizan operaciones de cálculo o de disco / red).

Escribí un artículo al respecto Java 8: fusión - hledáme neNULLové hodnoty - (escrito en checo, pero espero que los ejemplos de código sean comprensibles para todos).


1
Buen artículo, sin embargo, sería bueno tenerlo en inglés.
Quantum

1
Hay algo en esa página de blog que no funciona con Google Translate. :-(
HairOfTheDog

5

Con guayaba puedes hacer:

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

que no arroja NPE si ambos ay bson null.

EDITAR: Me equivoqué, arroja NPE. La forma correcta según lo comentado por Michal Čizmazia es:

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

1
Hola, sí:java.lang.NullPointerException: use Optional.orNull() instead of Optional.or(null)
Michal Čizmazia

1
Esto hace el truco:Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull()
Michal Čizmazia

4

Solo para completar, el caso de "varias variables" es realmente posible, aunque no es elegante en absoluto. Por ejemplo, para las variables o, py q:

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

Tenga en cuenta el uso de orElseGet()asistir al caso de que o, pyq no son variables sino expresiones caros o con efectos secundarios no deseados.

En el caso más general 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

Esto puede generar expresiones excesivamente largas. Sin embargo, si estamos tratando de mudarnos a un mundo sin él null, entonces v[i]lo más probable es que ya seamos de tipo Optional<String>, en lugar de simplemente String. En este caso,

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

o en el caso de expresiones:

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

Por otra parte, si también se está moviendo a un estilo funcional declarativa, o, p, y qdebe ser de tipo Supplier<String>como en:

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

Y luego todo se coalescereduce simplemente ao.get() .

Para un ejemplo más 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()Y ageFromInput()sería ya volverOptional<Integer> , naturalmente.

Y luego se coalesceconvierte effectiveAge.get()o simplemente effectiveAgesi estamos contentos con unSupplier<Integer> .

En mi humilde opinión, con Java 8 veremos más y más código estructurado de esta manera, ya que es extremadamente autoexplicativo y eficiente al mismo tiempo, especialmente en casos más complejos.

Echo de menos una clase Lazy<T>que invoca Supplier<T>solo una vez, pero perezosamente, así como la coherencia en la definición de Optional<T>(es decir Optional<T>, Optional<T>operadores, o incluso Supplier<Optional<T>>).


4

Puedes probar esto:

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

Basado en esta respuesta


3

¿Qué tal usar proveedores cuando quieres evitar evaluar algún método costoso?

Me gusta esto:

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

Y luego usarlo así:

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

También puede usar métodos sobrecargados para las llamadas con dos, tres o cuatro argumentos.

Además, también podría usar transmisiones con algo como esto:

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

¿Por qué envolver el primer argumento en un Suppliersi será inspeccionado de todos modos? Por el bien de la uniformidad?
Inego

0

Qué tal si:

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

Java ArrayList permite convenientemente entradas nulas y esta expresión es coherente independientemente del número de objetos a considerar. (De esta forma, todos los objetos considerados deben ser del mismo tipo).


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

2
Dios, odio los genéricos. Vi lo que significaba el tuyo desde el principio. ¡Tuve que mirar a @ LES2 dos veces para darme cuenta de que estaba haciendo lo mismo (y probablemente "mejor")! +1 para mayor claridad
Bill K

Sí, los genéricos son el camino a seguir. Pero no estoy tan familiarizado con las complejidades.
Eric

10
Hora de aprender genéricos :-). Hay poca diferencia entre el ejemplo de @ LES2 y este, aparte de T en lugar de Object. -1 para construir una función que obligará a devolver el valor de retorno a Double. También para nombrar un método Java en mayúsculas, que puede estar bien en SQL, pero no es un buen estilo en Java.
Avi

1
Me doy cuenta de que todo en mayúsculas es una mala práctica. Solo le estaba mostrando al OP cómo escribir una función con el nombre que solicitaron. De acuerdo, el reparto Doubleestá lejos de ser ideal. Simplemente no sabía que las funciones estáticas podrían recibir parámetros de tipo. Pensé que solo eran clases.
Eric
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.