การตรวจสอบค่าว่างหลายรายการใน Java 8


99

ฉันมีรหัสด้านล่างซึ่งค่อนข้างน่าเกลียดสำหรับการตรวจสอบค่าว่างหลายรายการ

String s = null;

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

ดังนั้นฉันจึงลองใช้Optional.ofNullablelike ด้านล่าง แต่ก็ยังยากที่จะเข้าใจหากมีคนอ่านรหัสของฉัน วิธีที่ดีที่สุดในการทำเช่นนั้นใน Java 8 คืออะไร

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

ใน Java 9 เราสามารถใช้Optional.ofNullableกับOR, แต่ใน Java8 จะมีวิธีการอื่น ๆ ?


4
orไวยากรณ์ Java9 String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4);ดูไม่ดีเท่าที่Stream.ofฉันต้องการ sya
Naman

2
@ OleV.V. ไม่มีอะไรผิดพลาด OP ได้รับรู้แล้วและกำลังมองหาสิ่งที่เฉพาะเจาะจงสำหรับ Java-8
Naman

3
ฉันรู้ว่าผู้ใช้กำลังขอโซลูชันเฉพาะ Java-8 แต่ในบันทึกทั่วไปฉันจะไปกับStringUtils.firstNonBlank()
Mohamed Anees A

3
ปัญหาคือ Java 8 / stream ไม่ใช่ทางออกที่ดีที่สุดสำหรับสิ่งนี้ รหัสนั้นมีกลิ่นเหมือน refactor ตามลำดับ แต่ไม่มีบริบทเพิ่มเติมก็ยากที่จะบอก สำหรับผู้เริ่มต้น - เหตุใดวัตถุสามชิ้นที่อาจเกี่ยวข้องกันอย่างใกล้ชิดจึงไม่มีอยู่ในคอลเล็กชัน
Bill K

2
@MohamedAneesA จะให้คำตอบที่ดีที่สุด (เป็นความคิดเห็น) แต่ไม่ได้ระบุแหล่งที่มาของ StringUtils ในกรณีนี้ ไม่ว่าในกรณีใดก็ตามหากคุณต้องมีสตริงเหล่านี้เป็นสตริงแยกกันการเข้ารหัสเป็นวิธี vargs เช่น "firstNonBlank" เหมาะอย่างยิ่งไวยากรณ์ภายในจะเป็นอาร์เรย์ที่ทำให้แต่ละลูปง่ายขึ้นโดยมีผลตอบแทนจากการค้นหา ค่าที่ไม่เป็นค่าว่างเล็กน้อยและชัดเจน ในกรณีนี้สตรีม java 8 เป็นสิ่งที่น่ารำคาญ พวกเขาล่อลวงให้คุณอินไลน์และทำให้บางสิ่งซับซ้อนขึ้นซึ่งควรเป็นวิธีการ / วนซ้ำง่ายๆ
Bill K

คำตอบ:


173

คุณสามารถทำได้ดังนี้:

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

16
นี้. คิดในสิ่งที่คุณต้องการไม่ใช่สิ่งที่คุณมี
Thorbjørn Ravn Andersen

21
ความเร็วเหนือศีรษะเท่าไหร่? การสร้างอ็อบเจ็กต์ Stream เรียก 4 วิธีการสร้างอาร์เรย์ชั่วคราว ( {str1, str2, str3}) ดูเหมือนช้ากว่าโลคัลมากifหรือ?:รันไทม์ Java สามารถปรับให้เหมาะสมได้ มีการเพิ่มประสิทธิภาพเฉพาะสตรีมบางอย่างในjavacและรันไทม์ Java ซึ่งทำให้เร็วเท่ากับ?:หรือไม่ ถ้าไม่ฉันจะไม่แนะนำโซลูชันนี้ในโค้ดที่มีความสำคัญต่อประสิทธิภาพ
pts

16
@pts สิ่งนี้มีแนวโน้มที่จะช้ากว่า?:โค้ดมากและฉันแน่ใจว่าคุณควรหลีกเลี่ยงในโค้ดที่มีผลต่อประสิทธิภาพ อย่างไรก็ตามมันอ่านได้ง่ายกว่ามากและคุณควรแนะนำใน IMO ของโค้ดที่ไม่สำคัญต่อประสิทธิภาพซึ่งฉันแน่ใจว่าทำมากกว่า 99% ของโค้ด
Aaron

7
@pts ไม่มีการเพิ่มประสิทธิภาพเฉพาะสตรีมทั้งในjavacหรือในรันไทม์ สิ่งนี้ไม่ได้กีดกันการเพิ่มประสิทธิภาพทั่วไปเช่นการแทรกโค้ดทั้งหมดตามด้วยการกำจัดการดำเนินการที่ซ้ำซ้อน โดยหลักการแล้วผลลัพธ์สุดท้ายอาจมีประสิทธิภาพเท่ากับนิพจน์เงื่อนไขธรรมดาอย่างไรก็ตามมันไม่น่าเป็นไปได้เลยที่มันจะไปถึงที่นั่นเนื่องจากรันไทม์จะใช้ความพยายามที่จำเป็นเฉพาะกับพา ธ โค้ดที่ร้อนแรงที่สุดเท่านั้น
Holger

22
ทางออกที่ดี! เพื่อเพิ่มความสามารถในการอ่านเล็กน้อยฉันขอแนะนำให้เพิ่มstr4พารามิเตอร์Stream.of(...)และใช้orElse(null)ในตอนท้าย
danielp

73

วิธีการเกี่ยวกับตัวดำเนินการตามเงื่อนไข

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

50
จริงๆแล้วเขากำลังมองหา "แนวทางที่ดีที่สุดในการทำสิ่งนี้ใน Java8" วิธีนี้สามารถใช้ได้ใน Java8 ดังนั้นทุกอย่างขึ้นอยู่กับความหมายของ OP โดย "ดีที่สุด" และเหตุผลใดที่เขาใช้เป็นฐานในการตัดสินใจว่าอะไรดีกว่ากัน
Stultuske

17
ฉันมักจะไม่ชอบเทอร์นารีที่ซ้อนกัน แต่มันดูสะอาดดี
JollyJoker

11
นี่เป็นความเจ็บปวดอย่างมากในการแยกวิเคราะห์สำหรับใครก็ตามที่ไม่ได้อ่านโอเปอเรเตอร์ที่ซ้อนกันทุกวัน
คิวบิ

2
@Cubic: ถ้าคุณคิดว่ามันเป็นเรื่องยากที่จะแยก, // take first non-null of str1..3เขียนความเห็นชอบ เมื่อคุณรู้ว่ามันทำอะไรมันจะกลายเป็นเรื่องง่ายที่จะเห็น
Peter Cordes

4
@Cubic แต่นี่เป็นรูปแบบซ้ำ ๆง่ายๆ เมื่อคุณแยกวิเคราะห์บรรทัดที่ 2 คุณจะแยกวิเคราะห์ข้อมูลทั้งหมดไม่ว่าจะรวมกี่กรณีก็ตาม และเมื่อคุณทำเสร็จแล้วคุณได้เรียนรู้ที่จะแสดงตัวเลือกที่คล้ายกันของกรณีต่างๆ 10 กรณีโดยสังเขปและค่อนข้างเรียบง่าย ครั้งต่อไปที่คุณจะเห็น?:บันไดคุณจะรู้ว่ามันทำอะไร (Btw โปรแกรมเมอร์ที่ใช้งานได้รู้ว่าสิ่งนี้เป็น(cond ...)ข้อ: มันมักจะเป็นยามตามด้วยค่าที่เหมาะสมที่จะใช้เมื่อยามเป็นจริง)
cmaster - คืนสถานะโมนิกา

35

คุณยังสามารถใช้ลูป:

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

27

คำตอบปัจจุบันดี แต่คุณควรใส่ไว้ในวิธียูทิลิตี้:

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

วิธีนี้อยู่ในUtilชั้นเรียนของฉันมาหลายปีทำให้โค้ดสะอาดขึ้นมาก:

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

คุณสามารถทำให้เป็นแบบทั่วไปได้:

@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 ฟังก์ชันนี้เรียกว่าcoalesceดังนั้นฉันจึงเรียกมันว่าในโค้ดของฉันด้วย จะเหมาะกับคุณหรือไม่นั้นขึ้นอยู่กับว่าคุณชอบ SQL มากแค่ไหน
Tom Anderson

5
หากคุณจะใส่มันลงในวิธียูทิลิตี้คุณก็อาจทำให้มันมีประสิทธิภาพได้เช่นกัน
Todd Sewell

1
@ToddSewell คุณหมายถึงอะไร?
Gustavo Silva

5
@GustavoSilva Todd อาจหมายความว่านี่เป็นวิธียูทิลิตี้ของฉันไม่มีประโยชน์ในการใช้Arrays.stream()เมื่อฉันสามารถทำเช่นเดียวกันกับ a forและ a != nullซึ่งมีประสิทธิภาพมากกว่า และท็อดด์ก็พูดถูก อย่างไรก็ตามเมื่อฉันเขียนรหัสวิธีนี้ฉันกำลังมองหาวิธีทำสิ่งนี้โดยใช้คุณสมบัติ Java 8 เช่นเดียวกับ OP ดังนั้นก็มีเช่นกัน
walen

4
@GustavoSilva ใช่โดยพื้นฐานแล้ว: หากคุณกำลังจะวางสิ่งนี้เป็นฟังก์ชันที่ใช้ประโยชน์เกี่ยวกับรหัสที่สะอาดไม่สำคัญอีกต่อไปดังนั้นคุณอาจใช้เวอร์ชันที่เร็วกว่า
Todd Sewell

13

ฉันใช้ฟังก์ชันตัวช่วยบางอย่างเช่น

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

จากนั้นรหัสประเภทนี้สามารถเขียนเป็น

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

1
ทำไมต้องเป็นv0พารามิเตอร์พิเศษ
tobias_k

1
@tobias_k พารามิเตอร์พิเศษเมื่อใช้ varargs เป็นวิธีการใช้สำนวนใน Java ที่ต้องการอาร์กิวเมนต์ 1 รายการขึ้นไปแทนที่จะเป็น 0 หรือมากกว่า (ดูข้อ 53 ของ Effective Java, Ed.3 ซึ่งใช้minเป็นตัวอย่าง) ฉันไม่ค่อยมั่นใจว่าสิ่งนี้เหมาะสมที่นี่
นิค

3
@ นิกใช่ฉันเดาได้มาก แต่ฟังก์ชั่นจะทำงานได้ดีเช่นกัน (ในความเป็นจริงทำงานเหมือนกันทุกประการ) ถ้าไม่มีมัน
tobias_k

1
เหตุผลสำคัญประการหนึ่งในการส่งอาร์กิวเมนต์แรกพิเศษคือต้องมีความชัดเจนเกี่ยวกับพฤติกรรมเมื่อส่งผ่านอาร์เรย์ ในกรณีนี้ฉันต้องการfirstNonNull(arr)ส่งคืน arr หากไม่เป็นโมฆะ หากเป็นเช่นfirstNonNull(T... vs)นั้นจะส่งคืนรายการแรกที่ไม่ใช่ค่าว่างใน arr แทน
Michael Anderson

4

โซลูชันที่สามารถนำไปใช้กับองค์ประกอบได้มากเท่าที่คุณต้องการ ได้แก่ :

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

คุณสามารถจินตนาการถึงวิธีแก้ปัญหาดังต่อไปนี้ แต่วิธีแรกช่วยให้มั่นใจได้non nullityสำหรับองค์ประกอบทั้งหมด

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

6
orElse เป็นแรงบันดาลใจstr4ว่ามันเป็นโมฆะจริง
Naman

1
@nullpointer หรือorElseNullซึ่งstr4มีค่าเท่ากันถ้าเป็นโมฆะ
tobias_k

3

คุณยังสามารถรวม Strings ทั้งหมดไว้ในอาร์เรย์ของ String จากนั้นทำ for loop เพื่อตรวจสอบและแยกออกจากลูปเมื่อได้รับมอบหมาย สมมติว่า s1, s2, s3, s4 เป็นสตริงทั้งหมด

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


s = s4;

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

แก้ไขเพื่อโยนในเงื่อนไขสำหรับค่าเริ่มต้นเป็น s4 หากไม่มีการกำหนด


คุณได้ละเว้นs4(หรือstr4) ซึ่งควรกำหนดให้sในท้ายที่สุดแม้ว่าจะเป็นโมฆะก็ตาม
displayName

3

ตามวิธีการและง่าย

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

และใช้เป็น:

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

ทำได้ง่ายด้วยอาร์เรย์และดูสวย


0

หากคุณใช้ Apache Commons Lang 3 จะสามารถเขียนได้ดังนี้:

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

การใช้ObjectUtils.firstNonNull(T...)ถูกนำมาจากคำตอบนี้ วิธีการที่แตกต่างกันนอกจากนี้ยังได้นำเสนอในที่เกี่ยวข้องกับคำถาม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.