ทำความเข้าใจกับ Spliterator, Collector และ Stream ใน Java 8


143

ฉันมีปัญหาในการเข้าใจStreamอินเตอร์เฟสใน Java 8 โดยเฉพาะอย่างยิ่งที่เกี่ยวข้องกับSpliteratorและCollectorอินเตอร์เฟส ปัญหาของฉันคือฉันไม่เข้าใจSpliteratorและCollectorอินเทอร์เฟซเลยและด้วยเหตุนี้Streamอินเทอร์เฟซยังค่อนข้างคลุมเครือสำหรับฉัน

อะไรคือ a Spliteratorและ a Collectorและฉันจะใช้ได้อย่างไร หากฉันยินดีที่จะเขียนของตัวเองSpliteratorหรือCollector(และอาจเป็นของฉันเองStreamในกระบวนการนั้น) ฉันควรทำอย่างไรและไม่ควรทำอย่างไร

ฉันอ่านตัวอย่างกระจัดกระจายไปทั่วเว็บ แต่เนื่องจากทุกอย่างที่นี่ยังใหม่และอาจมีการเปลี่ยนแปลงตัวอย่างและแบบฝึกหัดก็ยังกระจัดกระจายมาก

คำตอบ:


142

คุณไม่ควรจัดการกับSpliteratorผู้ใช้ มันควรจะจำเป็นถ้าคุณกำลังเขียนCollectionประเภทตัวเองและยังตั้งใจที่จะเพิ่มประสิทธิภาพการดำเนินงาน parallelized กับพวกเขา

สำหรับสิ่งที่คุ้มค่า a Spliteratorคือวิธีการใช้งานองค์ประกอบของคอลเลกชันในลักษณะที่ง่ายต่อการแยกส่วนของคอลเลกชันเช่นเนื่องจากคุณขนานและต้องการให้เธรดหนึ่งชิ้นทำงานในส่วนหนึ่งของคอลเลกชัน หัวข้อหนึ่งที่จะทำงานในส่วนอื่น ๆ ฯลฯ

คุณไม่ควรบันทึกค่าชนิดStreamไว้ที่ตัวแปร Streamเป็นสิ่งที่คล้ายกันเนื่องจากเป็นIteratorวัตถุที่ใช้ครั้งเดียวที่คุณมักใช้ในสายโซ่คล่องแคล่วเหมือนในตัวอย่าง Javadoc:

int sum = widgets.stream()
                  .filter(w -> w.getColor() == RED)
                  .mapToInt(w -> w.getWeight())
                  .sum();

Collectorเป็นรุ่นที่เป็นไปได้มากที่สุดโดยทั่วไปและเป็นนามธรรมของการดำเนินการ "ลด" a แผนที่ / ย่อ; โดยเฉพาะอย่างยิ่งจำเป็นต้องสนับสนุนขั้นตอนการขนานและการสรุป ตัวอย่างของCollectors รวมถึง:

  • ข้อสรุปเช่น Collectors.reducing(0, (x, y) -> x + y)
  • StringBuilder ต่อท้ายเช่น Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)

31
Spliterator ยังมีวิธีการสตรีม Iterable ที่ไม่ใช่คอลเล็กชัน
Bohemian

2
ฉันหมายถึง "การลดการใช้งานในแง่ที่คำนั้นมีความหมายในแผนที่ / ลด"
Louis Wasserman

1
เป็นCollectors.ofวิธีการเก่ารุ่นเบต้าที่ถูกลบออกหรือกำลังฉันไม่มีอะไร? เพื่อความสมบูรณ์สามารถเขียนเป็น(x,y) -> x+y Integer::sum
Jean-François Savard

3
เอ้อไม่ขอโทษมันเป็น Collector.of ไม่ใช่ Collector.of
Louis Wasserman

2
ตัวอย่างของนักสะสมของคุณจะมีประโยชน์มากขึ้นถ้าคุณจะอธิบายว่านักสะสมของคุณทำอะไร
MiguelMunoz

90

Spliterator โดยทั่วไปหมายถึง "Iterator splittable"

เธรดเดี่ยวสามารถสำรวจ / ประมวลผล Spliterator ทั้งหมด แต่ Spliterator ยังมีวิธีการtrySplit()ที่จะ "แยก" ส่วนสำหรับคนอื่น ๆ (โดยทั่วไปแล้วเป็นเธรดอื่น) ในการประมวลผลโดยปล่อยให้ spliterator ปัจจุบันทำงานน้อยลง

Collectorรวมคุณสมบัติของreduceฟังก์ชั่น (ของแผนที่ลดชื่อเสียง) กับค่าเริ่มต้นและฟังก์ชั่นเพื่อรวมสองผลลัพธ์ (ดังนั้นการเปิดใช้งานผลลัพธ์จาก Spliterated ลำธารของการทำงานที่จะรวมกัน)

ตัวอย่างเช่น Collector พื้นฐานส่วนใหญ่จะมี vaue เริ่มต้นเป็น 0 เพิ่มจำนวนเต็มลงในผลลัพธ์ที่มีอยู่และจะรวมผลลัพธ์สองรายการด้วยการเพิ่ม ดังนั้นการสรุปกระแสของจำนวนเต็มแยก

ดู:


ค่าที่จะรวมผลลัพธ์สองรายการ?
Jason Law

@ JasonLaw - ชี้แจง! ขอบคุณสำหรับคำแนะนำ
โทมัส W

5

ต่อไปนี้เป็นตัวอย่างของการใช้ตัวสะสมที่กำหนดไว้ล่วงหน้าเพื่อทำงานลดความไม่แน่นอนที่พบบ่อย:

 // Accumulate names into a List
 List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());

 // Accumulate names into a TreeSet
 Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));

 // Convert elements to strings and concatenate them, separated by commas
 String joined = things.stream()
                       .map(Object::toString)
                       .collect(Collectors.joining(", "));

 // Compute sum of salaries of employee
 int total = employees.stream()
                      .collect(Collectors.summingInt(Employee::getSalary)));

 // Group employees by department
 Map<Department, List<Employee>> byDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment));

 // Compute sum of salaries by department
 Map<Department, Integer> totalByDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                                               Collectors.summingInt(Employee::getSalary)));

 // Partition students into passing and failing
 Map<Boolean, List<Student>> passingFailing =
     students.stream()
             .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

2
นี่ไม่ได้ตอบคำถามของ Op รวมทั้งไม่มีคำอธิบายหรือคำอธิบายของโพสต์ของคุณ
ซิด

4

อินเตอร์เฟซSpliterator- เป็นคุณลักษณะหลักของลำธาร

วิธีการstream()และparallelStream()ค่าเริ่มต้นจะแสดงในCollectionอินเทอร์เฟซ วิธีการเหล่านี้ใช้ Spliterator ผ่านการเรียกไปที่spliterator():

...

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

default Stream<E> parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}

...

Spliterator เป็นตัววนซ้ำภายในที่แบ่งกระแสข้อมูลออกเป็นส่วนเล็ก ๆ ชิ้นส่วนขนาดเล็กเหล่านี้สามารถประมวลผลแบบขนาน

เหนือสิ่งอื่นใดมีสองสิ่งที่สำคัญที่สุดที่จะเข้าใจ Spliterator:

  • boolean tryAdvance(Consumer<? super T> action) ต่างจาก the Iteratorมันพยายามที่จะดำเนินการกับองค์ประกอบต่อไป trueถ้าการดำเนินการดำเนินการประสบความสำเร็จในผลตอบแทนที่วิธีการ มิฉะนั้นส่งคืนfalse- นั่นหมายความว่าไม่มีองค์ประกอบหรือส่วนท้ายของสตรีม

  • Spliterator<T> trySplit() เมธอดนี้อนุญาตให้แบ่งชุดข้อมูลเป็นชุดเล็ก ๆ จำนวนมากตามเกณฑ์อย่างน้อยหนึ่งข้อ (ขนาดไฟล์จำนวนบรรทัด ฯลฯ )


´หากการดำเนินการประสบความสำเร็จ .. ´ คุณควรตั้งรหัสใหม่นี้ tryAdvance javadocนั้นชัดเจนขึ้น: ´ถ้ามีองค์ประกอบที่เหลืออยู่ให้ดำเนินการตามที่กำหนดไว้โดยคืนค่าจริง อื่น ๆ ผลตอบแทน false.'
Piro กล่าวว่าคืนสถานะโมนิก้า
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.