2010-05-04 12 views
108

¿Hay un equivalente Java de la función COALESCE de SQL? Es decir, ¿hay alguna forma de devolver el primer valor no nulo de varias variables?¿Cómo obtener el primer valor no nulo en Java?

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, b, y c - en este caso, sería volver b, o 4.4. (Algo así como el método sql - devuelve COALESCE(a,b,c)). Sé que puedo hacerlo de forma explícita con algo como:

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

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

+2

Usted no debe necesitar una función como esta ya que genally no calcularía 'c' si 'b' tiene la respuesta que desea. es decir, no construirías una lista de posibles respuestas solo para mantener una. –

+0

Advertencia: no todos los cortocircuitos RDBMS en COALESCE. Oracle solo recientemente comenzó a hacerlo. –

+2

@ BrainSlugs83 ¿En serio? Java debería? –

Respuesta

87

No, no lo hay.

Lo más cerca que se puede conseguir es:

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

Por razones de eficiencia, que puede manejar los casos más 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 ... 
} 
+1

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 de mano llena de artículos, que sospecho que será de uso común. – les2

+0

Cool. Gracias. En ese caso, probablemente me ceñiré a los operadores condicionales anidados en este caso, ya que es el único momento en que debe usarse y el método definido por el usuario sería excesivo ... – froadie

+7

Aún lo sacaría en un método de ayuda privada en lugar de dejar un bloque condicional de "aspecto aterrador" en el código - "¿qué hace eso?" De esa forma, 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 nombrado ayuda a documentar el intento del código, lo cual siempre es bueno, IMO. (y la sobrecarga de la versión no var-args es probablemente apenas mensurable.) – les2

0
Object coalesce(Object... objects) 
{ 
    for(Object o : object) 
     if(o != null) 
      return o; 
    return null; 
} 
+2

Dios odio genéricos. Vi lo que los tuyos significaron de inmediato. Tuve que mirar @ LES2 dos veces para descubrir que estaba haciendo lo mismo (¡y probablemente "mejor")! +1 por claridad –

+0

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

+10

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

22

A raíz de la respuesta de les2, puede eliminar alguna repetición 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); 
} 
+4

+1 por bonita. No estoy seguro acerca de los beneficios de eficiencia sobre el ciclo simple, pero si va a obtener alguna pequeña eficiencia de esta manera, bien podría ser bonito. –

+3

de esta manera hace que sea mucho menos doloroso y menos propenso a errores para escribir las variantes sobrecargadas! – les2

+2

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

50

Si está usando Guava, puede usar MoreObjects.firstNonNull(T...).

+38

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

+1

Buen comentario, Jake. Esta NullPointerException a menudo restringe el uso de Objects.firstNonNull. Sin embargo, es el enfoque de Guava para evitar nulos en absoluto. –

+4

Ese método ahora está en desuso, y la alternativa recomendada es [MoreObjects.firstNonNull] (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/MoreObjects.html#firstNonNull (T,% 20T)) – davidwebster48

7

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

Algunos función como esta

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

se debe utilizar antes de la compilación en código de bytes no debe ser un preprocesador que encuentran usos de esta función “se unen” y lo reemplaza con la construcción como

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

Actualización 2014-09-02:

Gracias a Java 8 y Lambdas hay possibi ¡La verdad es que se unen en Java! Incluyendo la característica crucial: las expresiones particulares se evalúan solo cuando es necesario; si las anteriores no son nulas, las siguientes no se evalúan (los métodos no se invocan, las operaciones de computación o de disco/red no se realizan).

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

+1

Buen artículo - sería bueno tenerlo en inglés, sin embargo. – quantum

+0

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

5

Con guayaba que puede hacer:

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

que no tirar NPE si ambos a y b son null.

EDITAR: Estaba equivocado, arroja NPE. La forma correcta como han comentado en Michal Čizmazia es:

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

Oye, lo hace: 'java.lang.NullPointerException: use Optional.orNull() en lugar de Optional.or (nulo)' –

+1

Esto hace el truco: 'Optional.fromNullable (a) .or (Optional.fromNullable (b)). orNull() ' –

29

Si sólo hay dos referencias para probar y está usando Java 8, podría 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.

Desafortunadamente su caso con "varias variables" no es posible con un método opcional. En su lugar, puede 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 
3

Solo por completitud, el caso de "varias variables" es de hecho posible, aunque no elegante en absoluto. Por ejemplo, para las variables o, p y q:

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

Tenga en cuenta el uso de orElseGet() asistir al caso de que o, p y q 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 largo. Sin embargo, si estamos tratando de movernos a un mundo sin null, entonces v[i] probablemente ya sean del tipo Optional<String>, en lugar de simplemente String. En este caso,

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

o en el caso de las 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 q debe 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 entonces todo el coalesce reduce simplemente a o.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() ya se volverían Optional<Integer>, naturalmente.

Y entonces el coalesce convierte effectiveAge.get() o simplemente effectiveAge si estamos contentos con un Supplier<Integer>.

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

no se pierda una clase Lazy<T> que invoca un Supplier<T> sólo una vez, pero con pereza, así como la coherencia en la definición de Optional<T> (es decir Optional<T> - Optional<T> operadores, o incluso Supplier<Optional<T>>).

0

¿Qué tal:

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

Java ArrayList permite convenientemente entradas nulas y esta expresión es constante independientemente de la cantidad de objetos a considerar. (En esta forma, todos los objetos considerados tienen que ser del mismo tipo.)

0

Usted puede intentar esto:

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

Basado en Christian Ullenboom respuesta

Cuestiones relacionadas