Java 8'de birden çok boş denetim


99

Birden fazla boş kontrol için biraz çirkin olan aşağıdaki koda sahibim.

String s = null;

if (str1 != null) {
    s = str1;
} else if (str2 != null) {
    s = str2;
} else if (str3 != null) {
    s = str3;
} else {
    s = str4;
}

Bu yüzden Optional.ofNullableaşağıdaki gibi kullanmayı denedim , ancak birinin kodumu okuyup okumadığını anlamak hala zor. Java 8'de bunu yapmak için en iyi yaklaşım nedir.

String s = Optional.ofNullable(str1)
                   .orElse(Optional.ofNullable(str2)
                                   .orElse(Optional.ofNullable(str3)
                                                   .orElse(str4)));

Java 9'da Optional.ofNullableile kullanabiliriz OR, Ama Java8'de başka bir yaklaşım var mı?


4
Java9 orsözdizimi , sya String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4);kadar iyi görünmüyor Stream.of.
Naman

2
@ OleV.V. Yanlış bir şey yok, OP zaten bunun farkında ve Java-8'e özgü bir şey arıyor.
Naman

3
Kullanıcının Java-8'e özel bir çözüm istediğini biliyorum, ancak genel bir not olarak şunu söyleyeyimStringUtils.firstNonBlank()
Mohamed Anees A

3
Sorun şu ki, Java 8 / akışları bunun için en iyi çözüm değil. Bu kod gerçekten bir refactor gibi kokuyor, ancak daha fazla bağlam olmadan bunu söylemek gerçekten zor. Yeni başlayanlar için - muhtemelen bu kadar yakından ilişkili olan üç nesne zaten bir koleksiyonda yer almıyor?
Bill K

2
@MohamedAneesA en iyi cevabı (yorum olarak) sağlardı ancak bu durumda StringUtils kaynağını belirtmedi. Her halükarda, bunları bir grup ayrı dizge olarak almanız ZORUNLUysa, onu "firstNonBlank" gibi bir vargs yöntemi olarak kodlamak idealdir, içindeki sözdizimi her döngü için basit bir döngü oluşturan bir dizi olacaktır. boş olmayan değer önemsiz ve açık. Bu durumda, java 8 akışları çekici bir baş belasıdır. basit bir yöntem / döngü olması gereken bir şeyi satır içi yapmaya ve karmaşıklaştırmaya teşvik ederler.
Bill K

Yanıtlar:


173

Bunu şu şekilde yapabilirsiniz:

String s = Stream.of(str1, str2, str3)
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(str4);

16
Bu. Sahip olduklarınızı değil, ihtiyacınız olanı düşünün.
Thorbjørn Ravn Andersen

21
Hız yükü ne kadar? Akış nesneleri oluşturmak, 4 yöntemi çağırmak, geçici diziler ( {str1, str2, str3}) oluşturmak, yerel ifveya ?:Java çalışma zamanının optimize edebileceği şekilde çok daha yavaş görünür . javacJava runtime'da onu kadar hızlı hale getiren bazı Stream'e özgü optimizasyon var mı ?:? Değilse, bu çözümü performans açısından kritik kodda önermeyeceğim.
pts

16
@pts bu, koddan daha yavaş olabilir ?:ve eminim performans açısından kritik kodda bundan kaçınmanız gerekir. Bununla birlikte, çok daha okunabilir ve bunu performans açısından kritik olmayan IMO kodunda tavsiye etmelisiniz, ki eminim ki kodun% 99'undan fazlasını oluşturur.
Aaron

7
@pts Ne javacçalışma zamanı içinde ne de akışa özgü optimizasyonlar yoktur . Bu, tüm kodun satır içine alınması gibi genel optimizasyonları ve ardından gereksiz işlemleri ortadan kaldırmayı engellemez. Prensipte, nihai sonuç düz koşullu ifadeler kadar verimli olabilir, ancak çalışma zamanı gerekli çabayı yalnızca en sıcak kod yollarında harcayacağından, oraya ulaşması pek olası değildir.
Holger

22
Harika çözüm! Okunabilirliği biraz arttırmak str4için parametrelere eklemenizi ve sonunda Stream.of(...)kullanmanızı öneririm orElse(null).
danielp

73

Üçlü koşullu operatör nasıl olur?

String s = 
    str1 != null ? str1 : 
    str2 != null ? str2 : 
    str3 != null ? str3 : str4
;

50
aslında, "bunu Java8'de yapmak için en iyi yaklaşımı" arıyor. Bu yaklaşım Java8'de kullanılabilir, bu yüzden hepsi sadece OP'nin "en iyi" ile ne anlama geldiğine ve neyin daha iyi olduğuna kararını hangi temele dayandırdığına bağlıdır.
Stultuske

17
Genelde iç içe geçmiş üçlülerden hoşlanmam ama bu oldukça temiz görünüyor.
JollyJoker

11
Bu, her gün iç içe geçmiş üçlü operatörleri okumayan herkes için ayrıştırılması gereken büyük bir acıdır.
Cubic

2
@Cubic: Ayrıştırmanın zor olduğunu düşünüyorsanız, gibi bir yorum yazın // take first non-null of str1..3. Ne yaptığını öğrendikten sonra, nasıl olduğunu görmek kolaylaşır.
Peter Cordes

4
@Cubic Yine de, bu tekrarlanan basit bir model. 2. satırı ayrıştırdıktan sonra, kaç vaka dahil edilmiş olursa olsun, her şeyi ayrıştırmış olursunuz. İşiniz bittiğinde, on farklı vakanın benzer bir seçimini kısa ve nispeten basit bir şekilde ifade etmeyi öğrendiniz. Bir dahaki sefere bir ?:merdiven gördüğünüzde , ne işe yaradığını bileceksiniz. (Btw, işlevsel programcılar bunu (cond ...)cümle olarak bilirler : Her zaman bir
bekçidir

35

Ayrıca bir döngü de kullanabilirsiniz:

String[] strings = {str1, str2, str3, str4};
for(String str : strings) {
    s = str;
    if(s != null) break;
}

27

Mevcut cevaplar güzel, ancak bunu gerçekten bir yardımcı yönteme koymalısınız:

public static Optional<String> firstNonNull(String... strings) {
    return Arrays.stream(strings)
            .filter(Objects::nonNull)
            .findFirst();
}

Bu yöntem yıllardır benim Utilsınıfımda, kodu daha temiz hale getiriyor:

String s = firstNonNull(str1, str2, str3).orElse(str4);

Hatta jenerik bile yapabilirsiniz:

@SafeVarargs
public static <T> Optional<T> firstNonNull(T... objects) {
    return Arrays.stream(objects)
            .filter(Objects::nonNull)
            .findFirst();
}

// Use
Student student = firstNonNull(student1, student2, student3).orElseGet(Student::new);

10
FWIW, SQL'de bu fonksiyon coalesce olarak adlandırılır , bu yüzden onu kodumda da adlandırıyorum . Bunun sizin için işe yarayıp yaramayacağı, gerçekten SQL'i ne kadar sevdiğine bağlıdır.
Tom Anderson

5
Eğer onu bir fayda yöntemine koyacaksanız, onu verimli hale de getirebilirsiniz.
Todd Sewell

1
@ToddSewell, ne demek istiyorsun?
Gustavo Silva

5
@GustavoSilva Todd muhtemelen şu anlama geliyor, bu benim bir faydalı yöntemim Arrays.stream(), aynı şeyi a forve a ile yapabildiğimde kullanmanın bir anlamı yok != null, ki bu daha verimli. Ve Todd haklı olurdu. Ancak, bu yöntemi kodladığımda, bunu Java 8 özelliklerini kullanarak yapmanın bir yolunu arıyordum, tıpkı OP gibi, işte bu var.
walen

4
@GustavoSilva Evet, temelde bu: Eğer bunu bir kullanım işlevi olarak koyacaksanız, temiz kodla ilgili endişeler artık o kadar önemli değil ve bu nedenle daha hızlı bir sürüm de kullanabilirsiniz.
Todd Sewell

13

Yardımcı bir işlev kullanıyorum, şunun gibi

T firstNonNull<T>(T v0, T... vs) {
  if(v0 != null)
    return v0;
  for(T x : vs) {
    if (x != null) 
      return x;
  }
  return null;
}

O zaman bu tür bir kod şu şekilde yazılabilir:

String s = firstNonNull(str1, str2, str3, str4);

1
Neden ekstra v0parametre?
tobias_k

1
@tobias_k Varargs kullanılırken ekstra parametre, Java'da 0 veya daha fazlası yerine 1 veya daha fazla argüman gerektirmenin deyimsel yoludur. ( minÖrnek olarak kullanılan Effective Java, Ed. 3. madde 53'e bakın .) Bunun burada uygun olduğuna daha az ikna oldum.
Nick

3
@Nick Evet, tahmin ettiğim kadarıyla, ancak işlev onsuz da (aslında tam olarak aynı şekilde davranır) çalışırdı.
tobias_k

1
Fazladan bir ilk bağımsız değişken iletmenin temel nedenlerinden biri, bir dizi iletildiğinde davranış hakkında açık olmaktır. Bu durumda, firstNonNull(arr)boş değilse arr'ı döndürmek istiyorum . Eğer öyleyse, firstNonNull(T... vs)bunun yerine arr içindeki ilk boş olmayan girdiyi döndürürdü.
Michael Anderson

4

İstediğiniz kadar elemana uygulanabilecek bir çözüm şunlar olabilir:

Stream.of(str1, str2, str3, str4)
      .filter(Object::nonNull)
      .findFirst()
      .orElseThrow(IllegalArgumentException::new)

Aşağıdaki gibi bir çözüm hayal edebilirsiniz, ancak ilki non nullitytüm unsurları sağlar

Stream.of(str1, str2, str3).....orElse(str4)

6
orElse str4aslında boş olmasına rağmen
Naman

1
@nullpointer Veya orElseNull, str4null ise aynı anlama gelir .
tobias_k

3

Ayrıca tüm Dizeleri bir String dizisi içinde toplayabilir, ardından atandıktan sonra döngüyü kontrol etmek ve kırmak için bir for döngüsü yapabilirsiniz. S1, s2, s3, s4'ün tümünün Dizeler olduğunu varsayarsak.

String[] arrayOfStrings = {s1, s2, s3};


s = s4;

for (String value : arrayOfStrings) {
    if (value != null) { 
        s = value;
        break;
    }
}

Hiçbiri atanmamışsa varsayılan olarak s4 olarak atılacak şekilde düzenlendi.


Sen atladık s4(veya str4) hangi atanacak gösterdiği tahmin etmektir snull olsa bile, sonunda.
displayName

3

Yönteme dayalı ve basit.

String getNonNull(String def, String ...strings) {
    for(int i=0; i<strings.length; i++)
        if(strings[i] != null)
             return s[i];
    return def;
}

Ve şu şekilde kullanın:

String s = getNonNull(str4, str1, str2, str3);

Dizilerle yapmak basit ve güzel görünüyor.


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.