Java'da ilk null olmayan değeri nasıl elde edebilirim?


154

SQL COALESCEişlevinin Java eşdeğeri var mı? Yani, birkaç değişkenin ilk null olmayan değerini döndürmenin bir yolu var mı?

Örneğin

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

Bir şekilde ilk boş olmayan bir değer dönecektir bir açıklama yapmak istiyorum a, bve c- bu durumda, bu dönecekti bveya 4.4. (Sql yöntemi gibi bir şey - dönüş COALESCE(a,b,c)). Açıkça böyle bir şeyle yapabileceğimi biliyorum:

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

Ama bunu gerçekleştirmek için yerleşik, kabul edilmiş herhangi bir işlev olup olmadığını merak ettim.


3
Eğer 'b' istediğiniz cevaba sahipse gerçekten 'c' hesaplayamayacağınız için böyle bir fonksiyona ihtiyacınız olmamalıdır. yani sadece bir tane tutmak için olası cevapların bir listesini oluşturmazsınız.
Peter Lawrey

Uyarı: COALESCE üzerinde tüm RDBMS kısa devresi değil. Oracle kısa süre önce yapmaya başladı.
Adam Gent

3
@ BrainSlugs83 Cidden mi? Java gerekir mi?
Dmitry Ginzburg

Yanıtlar:


108

Hayır, yok.

Alabileceğiniz en yakın şey:

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

Etkili nedenlerden dolayı, yaygın vakaları aşağıdaki gibi ele alabilirsiniz:

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
Yukarıda bahsettiğim verimlilik nedenleri, yöntemin var arg sürümünü her başlattığınızda bir dizi ayırma gerçekleşecek olmasıdır. Bu yaygın kullanım olacağından şüphelendiğim öğelerin el-doluları için israf olabilir.
les2

Güzel. Teşekkürler. Bu durumda, muhtemelen bu durumda iç içe koşullu işleçlere bağlı kalacağım çünkü bu sadece kullanılması gereken zaman ve kullanıcı tanımlı yöntem aşırıya
kaçacak

8
Hala kod "korkunç görünümlü" koşullu bir blok bırakmak yerine özel bir yardımcı yöntem içine dışarı çekmek istiyorum - "ne yapar?" bu şekilde, tekrar kullanmanız gerekirse, yöntemi yardımcı program sınıfına taşımak için IDE'nizdeki yeniden düzenleme araçlarını kullanabilirsiniz. adlandırılmış yönteme sahip olmak, kodun amacını belgelemeye yardımcı olur. (ve değişken olmayan sürümlerin ek yükü muhtemelen neredeyse ölçülebilir.)
les2

10
Dikkat: In coalesce(a, b), eğer bkarmaşık bir ifade ise ve adeğil null, bhala değerlendirilir. ?: Koşullu işleç için durum böyle değildir. Bu cevaba bakınız .
Pang

bu, her türlü argümanın birleşme çağrısından önce performans açısından nedensiz olarak yeniden hesaplanmasını gerektirir
Ivan G.


59

Kontrol edilecek yalnızca iki değişken varsa ve Guava kullanıyorsanız, MoreObjects.firstNonNull (önce T, T saniye) kullanabilirsiniz .


49
Objects.firstNonNull yalnızca iki bağımsız değişken alır; Guava'da varargs eşdeğeri yoktur. Ayrıca, her iki bağımsız değişken de boşsa bir NullPointerException özel durumu oluşturur - bu istenebilir veya istenmeyebilir.

2
İyi yorum Jake. Bu NullPointerException genellikle Objects.firstNonNull kullanımını kısıtlar. Bununla birlikte, Guava'nın null'lardan kaçınmak yaklaşımı.
Anton Shchastnyi

4
Bu yöntem artık kullanımdan kaldırılmıştır ve önerilen alternatif MoreObjects.firstNonNull
davidwebster48

1
NPE istenmeyen ise, o zaman bu cevaba
OrangeDog

51

Sınamak için yalnızca iki başvuru varsa ve Java 8 kullanıyorsanız

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

Statik İsteğe Bağlı içe aktarırsanız ifade çok kötü değil.

Ne yazık ki "birkaç değişkenli" durumunuz İsteğe bağlı bir yöntemle mümkün değildir. Bunun yerine şunları kullanabilirsiniz:

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

LES2'nin cevabından sonra, aşırı yüklenmiş fonksiyonu çağırarak verimli versiyondaki bazı tekrarları ortadan kaldırabilirsiniz:

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
Güzel için +1. Basit döngü üzerindeki verimlilik faydalarından emin değilim, ancak bu şekilde küçük bir verimlilik ekleyecekseniz, güzel de olabilir.
Carl Manaster

3
Bu şekilde, aşırı yüklenmiş varyantları yazmayı çok daha az acı verici ve daha az hata yapar!
les2

2
Verimli versiyonun amacı, bir diziyi kullanarak bellek ayıran belleği boşa harcamamaktı varargs. Burada, her iç içe coalesce()çağrı için bir yığın çerçeve oluşturarak belleği boşa harcıyorsunuz . Arama coalesce(a, b, c, d, e), hesaplamak için 3 adede kadar yığın karesi oluşturur.
Luke

10

Bu durum bazı önişlemciler gerektirir. Çünkü null olmayan ilk değeri seçen bir işlev (statik yöntem) yazarsanız, tüm öğeleri değerlendirir. Bazı öğelerin yöntem çağrısı olması sorun olur (zaman pahalı yöntem çağrıları olabilir). Ve bu yöntemler, önlerindeki herhangi bir öğe boş olmasa bile çağrılır.

Bunun gibi bazı işlevler

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

kullanılmalıdır, ancak bayt kodunu derlemeden önce bu “birleşme fonksiyonunun” kullanımlarını bulan ve onun yerine

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

2014-09-02 Güncellemesi:

Java 8 ve Lambdas sayesinde Java'da gerçek bir birleşim var! Önemli özellik dahil: belirli ifadeler yalnızca gerektiğinde değerlendirilir - eğer daha önce boş değilse, aşağıdaki ifadeler değerlendirilmez (yöntemler çağrılmaz, hesaplama veya disk / ağ işlemleri yapılmaz).

Bu konuda bir makale yazdım Java 8: coalesce - hledáme neNULLové hodnoty - (Çekçe yazılmış, ancak umarım kod örnekleri herkes için anlaşılabilir).


1
Güzel makale - yine de İngilizce olması güzel olurdu.
kuantum

1
Bu blog sayfasında Google Çeviri ile çalışmayan bir şey var. :-(
HairOfTheDog

5

Guava ile şunları yapabilirsiniz:

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

Her iki takdirde NPE atmaz hangi ave bvardır null.

EDIT: Yanılmışım, NPE atıyor. Michal mazizmazia'nın yorumladığı gibi :

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

1
Hey, öyle:java.lang.NullPointerException: use Optional.orNull() instead of Optional.or(null)
Michal Čizmazia

1
Bu hile yapar:Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull()
Michal Čizmazia

4

Tam olması için, "çok değişkenli" durum gerçekten de mümkün olmasa da mümkün. Örneğin, değişkenlerin o, pve q:

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

Dikkatini çekerim orElseGet()bakılırken o, pve qdeğişkenler ancak ifadeleri pahalı ya da istenmeyen yan etkiler ile ya da değildir.

En genel durumda 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

Bu, aşırı uzun ifadeler üretebilir. Onsuz bir dünyaya taşımak için çalışıyoruz Ancak, nulldaha sonra v[i]büyük olasılıkla zaten türdedir Optional<String>basitçe aksine, String. Bu durumda,

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

veya ifadeler söz konusu olduğunda:

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

Ayrıca fonksiyonel-bildirge stiline hareket ediyor Dahası, eğer o, pve qtipte olmalıdır Supplier<String>gibi:

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

Ve sonra bütün coalescebasitçe azalır o.get().

Daha somut bir örnek için:

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()ve ageFromInput()zaten Optional<Integer>doğal olarak geri dönecekti .

Ve sonra coalesceolur effectiveAge.get()ya da sadece bir effectiveAgeile mutlu olursak Supplier<Integer>.

IMHO, Java 8 ile daha fazla kodun bu şekilde yapılandırıldığını göreceğiz, çünkü aynı zamanda son derece açıklayıcı ve verimli, özellikle daha karmaşık durumlarda.

Ben sadece bir kez, ama tembel olarak, yanı sıra (yani - operatörler, hatta ) tanımında tutarlılık Lazy<T>çağıran bir sınıf özledim .Supplier<T>Optional<T>Optional<T>Optional<T>Supplier<Optional<T>>


4

Bunu deneyebilirsiniz:

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

Bu yanıta dayanarak


3

Pahalı bir yöntemi değerlendirmekten kaçınmak istediğinizde tedarikçileri kullanmaya ne dersiniz?

Bunun gibi:

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

Ve sonra böyle kullanmak:

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

İki, üç veya dört bağımsız değişkeni olan çağrılar için aşırı yüklenmiş yöntemleri de kullanabilirsiniz.

Ayrıca, akışları aşağıdaki gibi bir şeyle de kullanabilirsiniz:

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

Neden ilk argümanı Supplieryine de incelenecekse bir sarmalıyız? Tekdüzelik uğruna?
Inego

0

Nasıl olur:

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

Java ArrayList uygun şekilde null girişlere izin verir ve bu ifade dikkate alınacak nesne sayısına bakılmaksızın tutarlıdır. (Bu formda, dikkate alınan tüm nesnelerin aynı türden olması gerekir.)


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

2
Tanrım jeneriklerden nefret ediyorum. Senin sopanın ne anlama geldiğini gördüm. Ben aynı şeyi (ve muhtemelen "daha iyi") olduğunu anlamak için iki kez @ LES2 bakmak zorunda kaldım! Netlik için +1
Bill K

Evet, jenerikler gitmenin yolu. Ama karmaşıklıklara aşina değilim.
Eric

10
Jenerik öğrenme zamanı :-). @ LES2'nin örneği ile bunun yerine Object yerine T dışında çok az fark vardır. Dönüş değerinin tekrar Double'a dökülmesini sağlayacak bir işlev oluşturmak için -1. Ayrıca, SQL'de iyi olabilecek, ancak Java'da iyi bir stil olmayan tüm kapaklarda bir Java yöntemini adlandırmak için.
Avi

1
Tüm kapakların kötü bir uygulama olduğunu anlıyorum. Ben sadece OP istedikleri adı altında bir fonksiyon yazmak için gösteriliyordu. Kabul, geri döküm Doubleideal olmaktan uzaktır. Statik fonksiyonlara tip parametreleri verilebileceğinin farkında değildim. Bunun sadece sınıflar olduğunu düşündüm.
Eric
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.