คำตอบ Eran ของการอธิบายความแตกต่างระหว่างสองหาเรื่องและสามหาเรื่องรุ่นreduce
ในอดีตจะช่วยลดStream<T>
การT
ในขณะที่หลังจะช่วยลดการStream<T>
U
แต่ก็ไม่ได้จริงอธิบายความจำเป็นสำหรับการทำงาน combiner เพิ่มเติมเมื่อลดการStream<T>
U
หนึ่งในหลักการออกแบบของ Streams API คือ API ไม่ควรแตกต่างกันระหว่างสตรีมแบบเรียงลำดับและแบบขนานหรือใส่อีกวิธีหนึ่ง API แบบใดแบบหนึ่งไม่ควรป้องกันสตรีมจากการทำงานอย่างถูกต้องไม่ว่าจะเป็นแบบเรียงลำดับหรือแบบขนาน หากลูกแกะของคุณมีคุณสมบัติที่เหมาะสม (เชื่อมโยง, ไม่รบกวน ฯลฯ ) สตรีมที่ทำงานเรียงตามลำดับหรือขนานควรให้ผลลัพธ์เดียวกัน
ก่อนอื่นเรามาพิจารณาการลดลงของสองรุ่น:
T reduce(I, (T, T) -> T)
การดำเนินการตามลำดับนั้นตรงไปตรงมา ค่าตัวตนI
คือ "สะสม" กับองค์ประกอบกระแส zeroth เพื่อให้ผลลัพธ์ ผลลัพธ์นี้จะถูกสะสมพร้อมกับองค์ประกอบสตรีมแรกที่จะให้ผลลัพธ์อื่นซึ่งจะถูกสะสมพร้อมกับองค์ประกอบสตรีมที่สองและอื่น ๆ หลังจากสะสมองค์ประกอบสุดท้ายแล้วผลลัพธ์สุดท้ายจะถูกส่งคืน
การใช้งานแบบขนานเริ่มต้นโดยแยกกระแสออกเป็นส่วน ๆ แต่ละเซกเมนต์ถูกประมวลผลด้วยเธรดของตัวเองตามลำดับที่ฉันอธิบายไว้ข้างต้น ตอนนี้ถ้าเรามี N เธรดเราก็จะได้ผลลัพธ์ N ตัวกลาง สิ่งเหล่านี้จำเป็นต้องลดลงเหลือเพียงผลลัพธ์เดียว เนื่องจากผลลัพธ์ระดับกลางแต่ละชนิดเป็นประเภท T และเรามีหลายอย่างเราจึงสามารถใช้ฟังก์ชันตัวสะสมเดียวกันเพื่อลดผลลัพธ์ระดับกลาง N เหล่านั้นให้เหลือเพียงผลลัพธ์เดียว
ตอนนี้ขอพิจารณาการดำเนินงานที่ลดลงสองหาเรื่องสมมุติที่ช่วยลดการStream<T>
U
ในภาษาอื่น ๆ สิ่งนี้เรียกว่าการดำเนินการ"fold"หรือ "fold-left" ดังนั้นนั่นคือสิ่งที่ฉันจะเรียกที่นี่ หมายเหตุสิ่งนี้ไม่มีอยู่ใน Java
U foldLeft(I, (U, T) -> U)
(โปรดทราบว่าค่าตัวตนI
เป็นประเภท U)
รุ่นต่อเนื่องของfoldLeft
จะเหมือนกับรุ่นต่อเนื่องreduce
ยกเว้นว่าค่ากลางจะเป็นประเภท U แทนประเภท T แต่เป็นอย่างอื่น (การfoldRight
ดำเนินการตามสมมติฐานจะคล้ายกันยกเว้นว่าการดำเนินการจะดำเนินการจากขวาไปซ้ายแทนจากซ้ายไปขวา)
foldLeft
ตอนนี้พิจารณารุ่นขนาน เริ่มกันเลยโดยแยกกระแสออกเป็นส่วน ๆ จากนั้นเราสามารถให้แต่ละเธรด N ลดค่า T ในเซ็กเมนต์ของมันเป็นค่ากลาง N ของประเภท U ได้แล้วตอนนี้อะไร? เราจะรับค่า N ประเภทของ U ลงมาเป็นผลลัพธ์เดียวของ type U ได้อย่างไร
สิ่งที่ขาดหายไปคือฟังก์ชั่นอื่นที่รวมผลลัพธ์หลายระดับกลางของประเภท U เป็นผลลัพธ์เดียวของประเภท U หากเรามีฟังก์ชั่นที่รวมสองค่า U เป็นหนึ่งนั่นก็เพียงพอแล้วที่จะลดจำนวนค่าใด ๆ ลดต้นฉบับด้านบน ดังนั้นการดำเนินการลดที่ให้ผลลัพธ์ของประเภทที่แตกต่างกันนั้นต้องการสองฟังก์ชัน:
U reduce(I, (U, T) -> U, (U, U) -> U)
หรือโดยใช้ไวยากรณ์ Java:
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
โดยสรุปหากต้องการลดขนานไปกับประเภทผลลัพธ์ที่แตกต่างกันเราต้องการฟังก์ชันที่สอง: ฟังก์ชันหนึ่งที่รวบรวมองค์ประกอบ T ถึงค่า U กลางและวินาทีที่รวมค่า U กลางไว้ในผลลัพธ์ U เดียว หากเราไม่ได้เปลี่ยนชนิดมันกลับกลายเป็นว่าฟังก์ชั่นสะสมเหมือนกันกับฟังก์ชั่นคอมบิวเตอร์ นั่นเป็นสาเหตุที่การลดประเภทเดียวกันมีเพียงฟังก์ชั่นสะสมและการลดลงเป็นประเภทอื่นจึงจำเป็นต้องมีฟังก์ชั่นสะสมและแยกตัว
ท้ายที่สุด Java ไม่ได้จัดเตรียมfoldLeft
และfoldRight
ดำเนินการเพราะมันหมายถึงการเรียงลำดับเฉพาะของการดำเนินการที่เรียงตามลำดับโดยเนื้อแท้ การปะทะกันนี้มีหลักการการออกแบบที่ระบุไว้ข้างต้นในการให้บริการ API ที่สนับสนุนการทำงานแบบต่อเนื่องและแบบขนาน