เราจะจัดการสตรีม jdk8 สำหรับค่า null ได้อย่างไร


88

สวัสดีเพื่อนนักพัฒนา Java

ฉันรู้ว่าหัวเรื่องอาจจะเล็กน้อยin advanceเนื่องจาก JDK8 ยังไม่เปิดตัว (และยังไม่ใช่สำหรับตอนนี้ .. ) แต่ฉันกำลังอ่านบทความเกี่ยวกับนิพจน์ Lambda และโดยเฉพาะส่วนที่เกี่ยวข้องกับ API คอลเล็กชันใหม่ที่เรียกว่า Stream

นี่คือตัวอย่างตามที่ระบุในบทความนิตยสาร Java (เป็นอัลกอริธึมประชากรนาก .. ):

Set<Otter> otters = getOtters();
System.out.println(otters.stream()
    .filter(o -> !o.isWild())
    .map(o -> o.getKeeper())
    .filter(k -> k.isFemale())
    .into(new ArrayList<>())
    .size());

คำถามของฉันคือจะเกิดอะไรขึ้นถ้าอยู่ตรงกลางของการวนซ้ำภายใน Set ตัวนากตัวใดตัวหนึ่งเป็นโมฆะ

ฉันคาดหวังว่า NullPointerException จะถูกโยนทิ้งไป แต่บางทีฉันอาจยังติดอยู่ในกระบวนทัศน์การพัฒนาก่อนหน้านี้ (ไม่สามารถใช้งานได้) ใครบางคนสามารถให้ความกระจ่างแก่ฉันได้ว่าควรจัดการอย่างไร

หากนี่เป็นการโยน NullPointerException จริงๆฉันพบว่าคุณลักษณะนี้ค่อนข้างอันตรายและจะต้องใช้เฉพาะดังต่อไปนี้:

  • ผู้พัฒนาเพื่อให้แน่ใจว่าไม่มีค่า null (อาจใช้. filter ก่อนหน้า (o -> o! = null))
  • ผู้พัฒนาเพื่อให้แน่ใจว่าแอปพลิเคชันจะไม่สร้าง Null otter หรือวัตถุ NullOtter พิเศษเพื่อจัดการ

ตัวเลือกที่ดีที่สุดคืออะไรหรือตัวเลือกอื่น ๆ ?

ขอบคุณ!


3
ฉันว่ามันขึ้นอยู่กับโปรแกรมเมอร์ที่จะทำสิ่งที่ถูกต้องที่นี่ JVM และคอมไพเลอร์สามารถทำได้มากเท่านั้น อย่างไรก็ตามการใช้งานคอลเล็กชันบางอย่างจะไม่อนุญาตให้ใช้ค่า null
fge

19
คุณสามารถใช้filter(Objects::nonNull)กับObjectsจากjava.utils
Benj

คำตอบ:


44

ความคิดในปัจจุบันดูเหมือนจะ "ยอม" โมฆะนั่นคือเพื่อให้โดยทั่วไปแม้ว่าการดำเนินการบางอย่างจะมีความอดทนน้อยกว่าและอาจลงเอยด้วยการโยน NPE ดูการอภิปรายเรื่องค่าว่างในรายชื่ออีเมลกลุ่มผู้เชี่ยวชาญของแลมบ์ดาไลบรารีโดยเฉพาะข้อความนี้ ฉันทามติเกี่ยวกับตัวเลือก # 3 ในเวลาต่อมา (พร้อมกับการคัดค้านที่น่าสังเกตจาก Doug Lea) ใช่แล้วความกังวลของ OP เกี่ยวกับท่อที่ระเบิดด้วย NPE นั้นถูกต้อง

ไม่ใช่เพื่ออะไรที่ Tony Hoare เรียกโมฆะว่า"Billion Dollar Mistake" การจัดการกับโมฆะเป็นความเจ็บปวดอย่างแท้จริง แม้จะมีคอลเล็กชันแบบคลาสสิก (โดยไม่ต้องพิจารณาแลมบ์ดาสหรือสตรีม) ค่าว่างก็เป็นปัญหาได้ ตามที่fgeกล่าวไว้ในความคิดเห็นบางคอลเลกชันอนุญาตให้มีค่าว่างและอื่น ๆ ไม่อนุญาต ด้วยคอลเล็กชันที่อนุญาตให้ใช้ null สิ่งนี้จะนำความไม่ชัดเจนมาสู่ API ตัวอย่างเช่นเมื่อใช้Map.get ()การส่งคืนค่าว่างจะระบุว่ามีคีย์อยู่และค่าเป็นโมฆะหรือไม่มีคีย์ เราต้องทำงานพิเศษเพื่อทำให้กรณีเหล่านี้ไม่ชัดเจน

การใช้ null ตามปกติคือการแสดงว่าไม่มีค่า แนวทางในการจัดการกับสิ่งนี้ที่นำเสนอสำหรับ Java SE 8 คือการนำเสนอjava.util.Optionalประเภทใหม่ซึ่งจะสรุปการมี / ไม่มีค่าพร้อมกับพฤติกรรมในการจัดหาค่าเริ่มต้นหรือการทิ้งข้อยกเว้นหรือการเรียกใช้ฟังก์ชันเป็นต้นหาก ไม่มีค่า Optionalถูกใช้โดย API ใหม่เท่านั้น แต่ทุกสิ่งทุกอย่างในระบบยังคงต้องทนกับความเป็นไปได้ของ null

คำแนะนำของฉันคือหลีกเลี่ยงการอ้างอิงว่างเปล่าให้มากที่สุดเท่าที่จะทำได้ ยากที่จะเห็นจากตัวอย่างว่าจะมีตัวนาก "null" ได้อย่างไร แต่ถ้าจำเป็นข้อเสนอแนะของ OP ในการกรองค่า null หรือการแมปกับอ็อบเจ็กต์ Sentinel ( รูปแบบวัตถุ Null ) เป็นแนวทางที่ดี


4
ทำไมต้องหลีกเลี่ยงโมฆะ? ใช้ในฐานข้อมูลอย่างกว้างขวาง
Raffi Khatchadourian

6
@RaffiKhatchadourian ใช่ nulls ถูกใช้ในฐานข้อมูล แต่มีปัญหาเท่ากัน ดูนี้และนี้และอ่านทุกคำตอบและความคิดเห็น พิจารณาผลกระทบของ SQL null ต่อนิพจน์บูลีนด้วยเช่นกัน: en.wikipedia.org/wiki/… ... นี่คือแหล่งที่มาของข้อผิดพลาดในการสืบค้น
Stuart Marks

1
@RaffiKhatchadourian เพราะnullห่วยนั่นเป็นเหตุผลว่าทำไม Acc. จากแบบสำรวจที่ฉันเห็น (หาไม่เจอ) NPE เป็นข้อยกเว้นอันดับ 1 ใน Java SQL ไม่ใช่ภาษาการเขียนโปรแกรมระดับสูงไม่สามารถใช้งานได้อย่างแน่นอนซึ่งดูหมิ่น null ดังนั้นจึงไม่สนใจ
Abhijit Sarkar

95

แม้ว่าคำตอบจะถูกต้อง 100% แต่คำแนะนำเล็ก ๆ ในการปรับปรุงการnullจัดการกรณีของรายการด้วยตัวเลือก :

 List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

ส่วนOptional.ofNullable(listOfStuff).orElseGet(Collections::emptyList)นี้จะช่วยให้คุณสามารถจัดการกับกรณีที่listOfStuffเป็นโมฆะและส่งคืน emptyList แทนการล้มเหลวด้วย NullPointerException


2
ฉันชอบสิ่งนี้หลีกเลี่ยงการตรวจสอบค่าว่างอย่างชัดเจน
คริส

1
นี่ดูดีที่สุดชัดเจน .. ดีและตรงกับสิ่งที่ฉันต้องการ
Abdullah Al Noman

เป็นการตรวจสอบค่าว่างอย่างชัดเจนด้วยวิธีอื่นเท่านั้น ถ้าอนุญาตให้เป็นโมฆะก็ควรเป็นตัวเลือกนั่นคือความคิดใช่ไหม แบนค่าว่างทั้งหมดด้วย optionals ยิ่งไปกว่านั้นการคืนค่า null แทนที่จะเป็นรายการที่ว่างเปล่าเป็นวิธีปฏิบัติที่ไม่ดีดังนั้นจึงแสดงรหัสสองกลิ่นหากฉันเห็นสิ่งนี้: ไม่มีการใช้ตัวเลือกและไม่มีการส่งคืนสตรีมว่าง และรุ่นหลังมีจำหน่ายมานานกว่า 20 ปีจึงโตเต็มที่ ...
Koos Gadellaa

จะเกิดอะไรขึ้นถ้าฉันต้องการคืนค่าว่างแทนรายการว่าง
Ashburn RK

@AshburnRK มันเป็นการปฏิบัติที่ไม่ดี คุณควรส่งคืนรายการที่ว่างเปล่า
Johnny

70

คำตอบของ Stuart ให้คำอธิบายที่ดี แต่ฉันต้องการยกตัวอย่างอื่น

ฉันพบปัญหานี้เมื่อพยายามดำเนินการreduceบนสตรีมที่มีค่า null (จริงๆแล้วมันLongStream.average()คือการลดประเภทหนึ่ง) เนื่องจากค่าเฉลี่ย () ส่งคืนOptionalDoubleฉันจึงสันนิษฐานว่าสตรีมอาจมีค่าว่าง แต่ NullPointerException ถูกโยนทิ้งไป นี่เป็นเพราะคำอธิบายของ Stuart เรื่อง null v ว่างเปล่า

ดังนั้นตามที่ OP แนะนำฉันจึงเพิ่มตัวกรองดังนี้:

list.stream()
    .filter(o -> o != null)
    .reduce(..);

หรือตามที่แทนเกนที่ระบุไว้ด้านล่างให้ใช้เพรดิเคตที่จัดเตรียมโดย Java API:

list.stream()
    .filter(Objects::nonNull)
    .reduce(..);

จากการอภิปรายรายชื่อผู้รับจดหมาย Stuart เชื่อมโยง: Brian Goetz บน nulls ใน Streams


19

หากคุณต้องการเพียงแค่ค่าตัวกรอง null ออกจากกระแสคุณก็สามารถใช้วิธีการในการอ้างอิงjava.util.Objects.nonNull (Object) จากเอกสารประกอบ:

วิธีการนี้มีอยู่เพื่อนำมาใช้เป็นคำกริยา ,filter(Objects::nonNull)

ตัวอย่างเช่น:

List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);

list.stream()
    .filter( Objects::nonNull )  // <-- Filter out null values
    .forEach( System.out::println );

สิ่งนี้จะพิมพ์:

Foo
Bar

7

ตัวอย่างวิธีหลีกเลี่ยง null เช่นใช้ filter ก่อน groupingBy

กรองอินสแตนซ์ null ออกก่อน groupingBy

นี่คือตัวอย่าง

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.