คอลเลกชันคอลเลกชันการรวบรวมนักสะสมของสะสม
คอลเลกชันเป็นส่วนต่อประสานบรรพบุรุษของคอลเลกชัน Java
คอลเลกชันเป็นคลาสเครื่องมือภายใต้แพ็คเกจ java.util ซึ่งหมายถึงวิธีการคงที่ต่าง ๆ สำหรับการประมวลผลคอลเลกชัน
java.util.stream.stream#collect (java.util.stream.collector <? super t, a, r>) เป็นฟังก์ชั่นของสตรีมที่รับผิดชอบในการรวบรวมสตรีม
java.util.stream.Collector เป็นอินเทอร์เฟซสำหรับการรวบรวมฟังก์ชั่นที่ประกาศฟังก์ชั่นของนักสะสม
Java.util.Comparators เป็นคลาสเครื่องมือนักสะสมที่มีชุดของการใช้งานนักสะสมในตัว
ฟังก์ชั่นของนักสะสม
คุณสามารถนึกถึงสตรีม Java8 เป็นตัววนซ้ำชุดข้อมูลแฟนซีและขี้เกียจ พวกเขาสนับสนุนการดำเนินงานสองประเภท: การดำเนินการระดับกลาง (เช่นตัวกรอง, แผนที่) และการดำเนินการเทอร์มินัล (เช่น Count, FindFirst, Foreach, ลดลง) การดำเนินการระดับกลางสามารถเชื่อมต่อเพื่อแปลงสตรีมหนึ่งเป็นอีกสตรีม การดำเนินการเหล่านี้ไม่ได้ใช้สตรีมและจุดประสงค์คือการสร้างท่อ ในทางตรงกันข้ามการดำเนินการเทอร์มินัลใช้คลาสส่งผลให้ผลลัพธ์สุดท้าย COLLECT เป็นการดำเนินการลดลงเช่นเดียวกับการลดมันสามารถยอมรับวิธีการต่าง ๆ เป็นพารามิเตอร์และสะสมองค์ประกอบในสตรีมเป็นผลลัพธ์สรุป วิธีการเฉพาะถูกกำหนดโดยการกำหนดอินเทอร์เฟซคอลเลคเตอร์ใหม่
นักสะสมที่กำหนดไว้ล่วงหน้า
ต่อไปนี้เป็นการสาธิตสั้น ๆ ของนักสะสมในตัว แหล่งข้อมูลจำลองมีดังนี้:
ARRAYLIST สุดท้าย <Sid> DISHES = Lists.newarrayList (อาหารจานใหม่ ("หมู", เท็จ, 800, ประเภท. meat), อาหารจานใหม่ ("เนื้อ", เท็จ, 700, ประเภท. meat), จานใหม่ ("ไก่", เท็จ, 400, meat.meat), อาหารใหม่ ( ผลไม้ ", จริง, 120, type.other), จานใหม่ (" พิซซ่า ", จริง, 550, type.other), จานใหม่ (" กุ้ง ", เท็จ, 300, type.fish), จานใหม่ (" ปลาแซลมอน ", เท็จ, 450, type.fish));ค่าสูงสุดค่าต่ำสุดค่าเฉลี่ย
// ทำไมต้องกลับมาเป็นทางเลือก? จะทำอย่างไรถ้าสตรีมเป็นโมฆะ? Optinal มีความหมายมากในเวลานี้ตัวเลือก <ShAST> MOSTCALORIEDISH = DISHES.STREAM (). max (comperator.ComparingInt (จาน :: getCalories)); ตัวเลือก <Sid> MINCALORIEDISH = DISHES.STREAM () dishes.stream (). collect (collector.avagingint (dish :: getcalories)); intsummarystatistics summarystatistics = dishes.stream (). collect (collector.summarizingint (จาน :: getcalories)); ค่าเฉลี่ยสองเท่า = สรุป Long count = summaryStatistics.getCount (); int max = summaryStatistics.getMax (); int min = summaryStatistics.getMin (); ผลรวมยาว = summaryStatistics.getSum ();
ตัวบ่งชี้ทางสถิติที่เรียบง่ายเหล่านี้มีฟังก์ชั่นนักสะสมในตัวโดยเฉพาะอย่างยิ่งสำหรับฟังก์ชั่น Unboxing ประเภทตัวเลขซึ่งจะมีราคาถูกกว่าการใช้งานประเภทบรรจุภัณฑ์โดยตรง
เชื่อมต่อนักสะสม
ต้องการรวบรวมองค์ประกอบของสตรีมหรือไม่?
// เชื่อมต่อสตริงโดยตรง join1 = dishes.stream (). แผนที่ (จาน :: getName) .collect (collectors.joining ()); // สตริงจุลภาค Join2 = dishes.stream (). แผนที่ (จาน :: getName) .collect
นักโสตทิลิสต์
รายการ <String> ชื่อ = dishes.stream (). แผนที่ (จาน :: getName) .collect (tolist ());
แมปสตรีมดั้งเดิมลงในสตรีมองค์ประกอบเดียวและรวบรวมเป็นรายการ
เครื่องโสเภณี
ตั้งค่า <ply> types = dishes.stream (). แผนที่ (จาน :: getType) .collect (collectors.toset ());
รวบรวมประเภทเป็นชุดและคุณสามารถทำซ้ำได้
Tomap
แผนที่ <type, dish> bytype = dishes.stream (). รวบรวม (tomap (dish :: gettype, d -> d));
บางครั้งอาจจำเป็นต้องแปลงอาร์เรย์เป็นแผนที่สำหรับแคชซึ่งอำนวยความสะดวกในการคำนวณและการได้มาหลายครั้ง TOMAP ให้ฟังก์ชั่นการสร้างวิธีการ K และ v. (โปรดทราบว่าการสาธิตข้างต้นเป็นหลุมคุณไม่สามารถใช้มันได้เช่นนี้ !!! โปรดใช้ TOMAP (ฟังก์ชั่น, ฟังก์ชั่น, BinaryOperator))))
ข้างต้นเป็นนักสะสมที่ใช้กันมากที่สุดและโดยทั่วไปก็เพียงพอแล้ว แต่ในฐานะผู้เริ่มต้นการทำความเข้าใจต้องใช้เวลา เพื่อให้เข้าใจอย่างแท้จริงว่าทำไมสิ่งนี้จึงสามารถใช้ในการรวบรวมได้คุณต้องตรวจสอบการใช้งานภายใน คุณจะเห็นว่านักสะสมเหล่านี้ขึ้นอยู่กับ java.util.stream.collectors.collectorimpl ซึ่งเป็นคลาสการใช้งานของนักสะสมที่กล่าวถึงในตอนต้น นักสะสมที่กำหนดเองจะได้เรียนรู้การใช้งานเฉพาะในภายหลัง
การลดที่กำหนดเอง
ไม่กี่คนก่อนหน้านี้เป็นกรณีพิเศษของกระบวนการลดที่กำหนดโดยวิธีการลดโรงงาน ในความเป็นจริงนักสะสมการลดสามารถใช้ในการสร้างนักสะสม ตัวอย่างเช่นค้นหาผลรวม
TotalCalories จำนวนเต็ม = dishes.stream (). รวบรวม (ลด (0, dish :: getcalories, (i, j) -> i + j)); // ใช้ฟังก์ชั่นในตัวแทนฟังก์ชั่นลูกศรจำนวนเต็มทั้งหมด
แน่นอนคุณสามารถใช้การลดโดยตรงได้โดยตรง
ตัวเลือก <integer> totalCalories3 = dishes.stream (). แผนที่ (จาน :: getCalories). ลด (จำนวนเต็ม :: ผลรวม);
แม้ว่าจะโอเคถ้าคุณพิจารณาประสิทธิภาพคุณควรเลือกสิ่งต่อไปนี้
int sum = dishes.stream (). maptoint (dish :: getCalories) .sum ();
เลือกทางออกที่ดีที่สุดตามสถานการณ์
ดังที่ได้กล่าวมาแล้วการเขียนโปรแกรมที่ใช้งานได้มักจะมีหลายวิธีในการดำเนินการเดียวกัน การใช้ Collector Collect นั้นซับซ้อนกว่าการใช้ API สตรีม ข้อได้เปรียบคือการรวบรวมสามารถให้ระดับที่สูงขึ้นของนามธรรมและการวางนัยทั่วไปและง่ายต่อการใช้ซ้ำและปรับแต่ง
คำแนะนำของเราคือการสำรวจการแก้ปัญหาที่แตกต่างกันเกี่ยวกับปัญหาในมือให้มากที่สุดเท่าที่จะเป็นไปได้เลือกมืออาชีพมากที่สุดเสมอซึ่งโดยทั่วไปเป็นการตัดสินใจที่ดีที่สุดในแง่ของการอ่านและประสิทธิภาพ
นอกเหนือจากการรับค่าเริ่มต้นการลดสามารถใช้รายการแรกเป็นค่าเริ่มต้น
ตัวเลือก <Sid> MOSTCALORIEDISH = DISHES.STREAM () .COLLECT (ลด ((D1, D2) -> D1.GetCalories ()> D2.GetCalories ()? D1: D2));
การลดลง
การใช้การลดนั้นค่อนข้างซับซ้อนและเป้าหมายคือการรวมค่าสองค่าเข้ากับค่าเดียว
สาธารณะคงที่ <t, u> collector <t,?, u> การลดลง (u identity, function <? super t,? ขยาย u> mapper, binaryoperator <u> op)
ก่อนอื่นฉันเห็น 3 นายสามัญ
คุณคือประเภทของค่าส่งคืน ตัวอย่างเช่นความร้อนที่คำนวณในการสาธิตข้างต้นคุณเป็นจำนวนเต็ม
เกี่ยวกับ t, t คือประเภทองค์ประกอบในสตรีม จากฟังก์ชั่นฟังก์ชั่นเราสามารถรู้ได้ว่าฟังก์ชั่นของ Mapper คือการรับพารามิเตอร์ T จากนั้นส่งคืนผลลัพธ์ที่สอดคล้องกับจานในการสาธิต
ในช่วงกลางของรายการทั่วไปที่มีตัวรวบรวมค่าส่งคืนสิ่งนี้แสดงถึงประเภทคอนเทนเนอร์ นักสะสมหลักสูตรต้องการคอนเทนเนอร์เพื่อจัดเก็บข้อมูล ที่นี่? ซึ่งหมายความว่าประเภทคอนเทนเนอร์ไม่แน่นอน อันที่จริงคอนเทนเนอร์ที่นี่คือ u []
เกี่ยวกับพารามิเตอร์:
ตัวตนคือค่าเริ่มต้นของประเภทค่าส่งคืนซึ่งสามารถเข้าใจได้ว่าเป็นจุดเริ่มต้นของตัวสะสม
Mapper เป็นฟังก์ชั่นของ MAP และความสำคัญของมันอยู่ในการแปลงสตรีมสตรีมให้เป็นสตรีมประเภทที่คุณต้องการ
OP เป็นฟังก์ชั่นหลักและฟังก์ชั่นของมันคือวิธีจัดการกับตัวแปรสองตัว ในหมู่พวกเขาตัวแปรแรกคือค่าสะสมซึ่งสามารถเข้าใจได้ว่าเป็นผลรวมและตัวแปรที่สองเป็นองค์ประกอบถัดไปที่จะคำนวณ ดังนั้นการสะสมจะเกิดขึ้นได้
นอกจากนี้ยังมีวิธีการโอเวอร์โหลดเพื่อละเว้นพารามิเตอร์แรกซึ่งหมายความว่าพารามิเตอร์แรกในสตรีมถูกใช้เป็นค่าเริ่มต้น
สาธารณะคงที่ <T> นักสะสม <t,?, ตัวเลือก <t>> การลด (BinaryOperator <T> op)
มาดูความแตกต่างระหว่างค่าส่งคืน t หมายถึงค่าอินพุตและประเภทค่าส่งคืนนั่นคือประเภทค่าอินพุตและประเภทค่าเอาต์พุตเหมือนกัน ความแตกต่างอีกประการหนึ่งคือตัวเลือก นี่เป็นเพราะไม่มีค่าเริ่มต้นและพารามิเตอร์แรกอาจเป็นโมฆะ เมื่อองค์ประกอบสตรีมเป็นโมฆะมันมีความหมายมากที่จะกลับมาเป็นตัวเลือก
เมื่อมองไปที่รายการพารามิเตอร์จะมีเพียง BinaryOperator เท่านั้น BinaryOperator เป็นอินเทอร์เฟซฟังก์ชั่นสามส่วนเป้าหมายคือการคำนวณพารามิเตอร์สองพารามิเตอร์ประเภทเดียวกันและค่าส่งคืนชนิดเดียวกัน มันสามารถเข้าใจได้ว่า 1> 2? 1: 2 นั่นคือค้นหาค่าสูงสุดของตัวเลขสองตัว การค้นหาค่าสูงสุดเป็นคำสั่งที่เข้าใจได้ง่าย คุณสามารถปรับแต่งนิพจน์แลมบ์ดาเพื่อเลือกค่าส่งคืน จากนั้นที่นี่มันคือการรับองค์ประกอบประเภท t ของสองสตรีมและส่งคืนค่าส่งคืนของประเภท T. มันก็โอเคที่จะใช้ผลรวมเพื่อทำความเข้าใจ
ในการสาธิตข้างต้นพบว่าฟังก์ชั่นของการลดและรวบรวมเกือบเท่ากันทั้งสองส่งคืนผลลัพธ์สุดท้าย ตัวอย่างเช่นเราสามารถใช้ลดเอฟเฟกต์โทลิสต์:
// ใช้ TolistCollector ด้วยตนเอง --- การลดลงของการลดกฎระเบียบที่ไม่เปลี่ยนรูป --- ไม่สามารถขนานรายการ <จำนวนเต็ม> แคลอรี่ = จานสเตม (). แผนที่ (จาน :: getCalories). ลด (arraylist ใหม่ <teger> (), (รายการ รายการ <จำนวนเต็ม> l2) -> {l1.addall (l2);ให้ฉันอธิบายการปฏิบัติข้างต้น
<u> คุณลด (u identity, bifunction <u,? super t, u> accumulator, binaryoperator <u> combiner);
U คือประเภทค่าส่งคืนนี่คือรายการ
bifunction <u,? Super T, U> Accumulator เป็นตัวสะสมและเป้าหมายคือการสะสมค่าและกฎการคำนวณสำหรับองค์ประกอบแต่ละองค์ประกอบ นี่คือการดำเนินการของรายการและองค์ประกอบและในที่สุดก็กลับรายการ นั่นคือเพิ่มองค์ประกอบในรายการ
BinaryOperator <u> Combiner เป็น combiner และเป้าหมายคือการรวมตัวแปรสองประเภทของค่าคืนค่าเป็นหนึ่ง นี่คือการควบรวมกิจการของสองรายการ
มีสองปัญหาเกี่ยวกับวิธีแก้ปัญหานี้: หนึ่งคือปัญหาความหมายและอีกปัญหาหนึ่งเป็นปัญหาในทางปฏิบัติ ปัญหาความหมายคือวิธีการลดลงมีจุดมุ่งหมายเพื่อรวมค่าสองค่าเพื่อสร้างค่าใหม่ซึ่งเป็นการลดที่ไม่เปลี่ยนรูป แต่การออกแบบวิธีการรวบรวมคือการเปลี่ยนคอนเทนเนอร์และสะสมผลลัพธ์ที่จะส่งออก ซึ่งหมายความว่าตัวอย่างโค้ดด้านบนกำลังใช้วิธีการลดลงเนื่องจากเปลี่ยนรายการเป็นตัวสะสมในสถานที่ ความหมายที่ไม่ถูกต้องในการใช้วิธีการลดยังสร้างปัญหาในทางปฏิบัติ: การลดลงนี้ไม่สามารถทำงานได้ในแบบคู่ขนานเนื่องจากการปรับเปลี่ยนโครงสร้างข้อมูลเดียวกันพร้อมกันโดยหลายเธรดอาจทำลายรายการเอง ในกรณีนี้หากคุณต้องการความปลอดภัยของเธรดคุณต้องจัดสรรรายการใหม่ในแต่ละครั้งและการจัดสรรวัตถุจะส่งผลกระทบต่อประสิทธิภาพ นี่คือเหตุผลที่การรวบรวมเหมาะสำหรับการแสดงการลดลงของภาชนะบรรจุที่ไม่แน่นอนและที่สำคัญกว่านั้นคือเหมาะสำหรับการดำเนินงานแบบขนาน
สรุป: การลดลงเหมาะสำหรับการลดภาชนะที่ไม่เปลี่ยนรูปแบบการรวบรวมเหมาะสำหรับการลดภาชนะที่ไม่แน่นอน รวบรวมเหมาะสำหรับการขนาน
การจัดกลุ่ม
ฐานข้อมูลมักจะพบความจำเป็นในการรวมกลุ่มและจัดเตรียมกลุ่มตามแบบดั้งเดิม ใน Java ถ้าคุณทำตามสไตล์การเรียนการสอน (เขียนลูปด้วยตนเอง) มันจะยุ่งยากมากและมีแนวโน้มที่จะเกิดข้อผิดพลาด Java 8 ให้บริการโซลูชั่นที่ใช้งานได้
ตัวอย่างเช่นจัดกลุ่มจานตามประเภท คล้ายกับ TOMAP ก่อนหน้า แต่ค่าการจัดกลุ่มไม่ใช่อาหาร แต่เป็นรายการ
แผนที่ <type, list <dish>> dishesbyType = dishes.stream (). collect (groupingby (dish :: gettype));
ที่นี่
สาธารณะคงที่ <t, k> collector <t,?, แผนที่ <k, รายการ <t >>> groupingby (ฟังก์ชั่น <? super t,? ขยาย k> classifier)
ตัวจําแนกพารามิเตอร์เป็นฟังก์ชั่นที่ออกแบบมาเพื่อรับพารามิเตอร์หนึ่งตัวและแปลงเป็นประเภทอื่น การสาธิตด้านบนคือการแปลงจานองค์ประกอบสตรีมเป็นประเภทประเภทจากนั้นจัดกลุ่มสตรีมตามประเภท การจัดกลุ่มภายในนั้นถูกนำไปใช้ผ่าน HashMap Groupingby (ตัวจําแนก, hashmap :: ใหม่, ดาวน์สตรีม);
นอกเหนือจากการจัดกลุ่มตามฟังก์ชันคุณสมบัติขององค์ประกอบสตรีมเองคุณยังสามารถปรับแต่งพื้นฐานการจัดกลุ่มเช่นการจัดกลุ่มตามช่วงความร้อน
เนื่องจากคุณรู้อยู่แล้วว่าพารามิเตอร์ของการจัดกลุ่มโดยฟังก์ชั่นและประเภทของฟังก์ชั่นพารามิเตอร์คือจานคุณสามารถปรับแต่งตัวแยกประเภทเป็น:
caloriclevel ส่วนตัว getCaloriclevel (dish d) {ถ้า (d.getCalories () <= 400) {return caloriclevel.diet; } อื่นถ้า (d.getCalories () <= 700) {return caloriclevel.normal; } else {return caloriclevel.fat; -เพียงแค่ผ่านในพารามิเตอร์
แผนที่ <caloriclevel, รายการ <dish>> dishesbylevel = dishes.stream () .collect (Groupingby (นี่ :: getCaloriclevel));
การจัดกลุ่มหลายระดับ
การจัดกลุ่มโดยใช้วิธีอื่น ๆ อีกหลายวิธีเช่น
สาธารณะคงที่ <t, k, a, d> collector <t,?, แผนที่ <k, d >> groupingby (ฟังก์ชั่น <? super t,?
มียาสามัญและน่ากลัวมากมาย มาทำความเข้าใจสั้น ๆ กันเถอะ ตัวจําแนกยังเป็นตัวจําแนกซึ่งได้รับประเภทองค์ประกอบของสตรีมและส่งคืนพื้นฐานที่คุณต้องการจัดกลุ่มนั่นคือความเป็น cardinality ของพื้นฐานการจัดกลุ่ม ดังนั้น t หมายถึงประเภทองค์ประกอบปัจจุบันของสตรีมและ k แสดงถึงประเภทองค์ประกอบของการจัดกลุ่ม พารามิเตอร์ที่สองคือดาวน์สตรีมและปลายน้ำเป็นนักสะสม ประเภทองค์ประกอบตัวสะสมนี้เป็นคลาสย่อยของ T, คอนเทนเนอร์ประเภทคอนเทนเนอร์คือ A และประเภทค่าผลตอบแทนลดลงคือ D นั่นคือ K ของกลุ่มที่ให้ผ่านตัวจําแนกและค่าของกลุ่มจะลดลงผ่านตัวสะสมของพารามิเตอร์ที่สอง เกิดขึ้นเพียงว่าซอร์สโค้ดของการสาธิตก่อนหน้านี้คือ:
สาธารณะคงที่ <t, k> collector <t,?, แผนที่ <k, รายการ <t >>> groupingby (ฟังก์ชั่น <? super t,? ขยาย k> classifier) {return groupingby (classifier, tolist ()); -ใช้ Tolist เป็นตัวเก็บลดและผลลัพธ์สุดท้ายคือรายการ <velm> ดังนั้นประเภทค่าของกลุ่มสิ้นสุดคือรายการ <vish> จากนั้นประเภทค่าสามารถถูกกำหนดโดยการลดการสะสมและมีนักสะสมลดลงหลายสิบล้านคน ตัวอย่างเช่นฉันต้องการจัดกลุ่มมูลค่าอีกครั้งและการจัดกลุ่มก็เป็นการลดลงเช่นกัน
// แผนที่การจัดกลุ่มหลายระดับ <ประเภท, แผนที่ <caloriclevel, รายการ <dish>> bytypeandcalory = dishes.stream (). รวบรวม (groupingby (จาน :: gettype, groupingby (นี่ :: getcaloriclevel))); bytypeandcalory.foreach System.out.println ("---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- System.out.println ("/t" + ระดับ);ผลการตรวจสอบคือ:
- - [dish (name = beef, มังสวิรัติ = false, แคลอรี่ = 700, type = meat)] ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - type = อื่น ๆ )]
สรุป: พารามิเตอร์หลักของการจัดกลุ่มโดยเป็นเครื่องกำเนิดไฟฟ้า K และเครื่องกำเนิดไฟฟ้า V เครื่องกำเนิด V สามารถเป็นนักสะสมประเภทใดก็ได้
ตัวอย่างเช่นเครื่องกำเนิด V สามารถคำนวณหมายเลขได้ดังนั้นจึงใช้การเลือกจำนวนเลือก (*) ในคำสั่ง SQL จากตาราง A โดยประเภท
แผนที่ <type, long> typescount = dishes.stream (). รวบรวม (groupingby (dish :: gettype, counting ())); system.out.println (typescount); ---------- {fish = 2, เนื้อ = 3, อื่น ๆ = 4} SQL Search Group คะแนนสูงสุด select MAX(id) from table A group by Type
แผนที่ <type, เสริม <dish>> MostCaloricByType = dishes.stream () .collect (Groupingby (dish :: getType, Maxby (compomator.comparingint (จาน :: getCalories))));
ทางเลือกที่นี่ไม่สมเหตุสมผลเพราะมันไม่เป็นโมฆะอย่างแน่นอน จากนั้นฉันก็ต้องนำมันออกมา ใช้การสะสมและแล้ว
แผนที่ <type, dish> mostcaloricbyType = dishes.stream () .collect (Groupingby (dish :: gettype, collectingandthen (maxby (compomator.comparingint (จาน :: getcalories)), เสริม :: get)));
ดูเหมือนว่าผลลัพธ์จะออกมาที่นี่ แต่ความคิดไม่เห็นด้วย มันรวบรวมสัญญาณเตือนสีเหลืองและเปลี่ยนเป็น:
แผนที่ <type, dish> mostcaloricbyType = dishes.stream () .collect (tomap (dish :: gettype, function.identity (), binaryoperator.maxby (เปรียบเทียบ (จาน :: getcalories))));
ใช่การจัดกลุ่มโดยกลายเป็น tomap คีย์ยังคงพิมพ์ค่ายังคงเป็นจาน แต่มีอีกหนึ่งพารามิเตอร์! - ที่นี่เราตอบสนองต่อหลุมในตอนแรก การสาธิต TOMAP ในตอนแรกนั้นใช้งานง่าย ถ้ามันถูกใช้จริง ๆ มันจะถูกฆ่า เรารู้ว่าการจัดระเบียบรายการใหม่ลงในแผนที่จะต้องเผชิญกับปัญหาเดียวกันอย่างหลีกเลี่ยงไม่ได้ เมื่อ K เหมือนกัน V จะแทนที่หรือไม่สนใจหรือไม่? วิธีการสาธิตก่อนหน้านี้คือการแทรก K อีกครั้งและโยนข้อยกเว้นโดยตรงเมื่อมี K อยู่:
java.lang.illegalstateException: คีย์คีย์ซ้ำ (ชื่อ = หมู, ผัก = เท็จ, แคลอรี่ = 800, ประเภท = เนื้อ) ที่ java.util.stream.collectors.lambda $ throwingmerger $ 0 (collector.java:133)
วิธีที่ถูกต้องคือการให้ฟังก์ชั่นสำหรับการจัดการความขัดแย้ง ในการสาธิตนี้หลักการของการจัดการความขัดแย้งคือการหาสิ่งที่ใหญ่ที่สุดซึ่งเป็นไปตามข้อกำหนดของเราสำหรับการจัดกลุ่มและค้นหาสิ่งที่ใหญ่ที่สุด (ฉันไม่ต้องการเรียนรู้การทำงานของ Java 8 อีกต่อไปฉันรู้สึกว่ามีปัญหาด้านประสิทธิภาพการทำงานทุกที่)
ดำเนินการแมปฐานข้อมูล SQL select sum(score) from table a group by Type
แผนที่ <ประเภท, จำนวนเต็ม> TotalCaloriesByType = dishes.stream () .collect (Groupingby (จาน :: gettype, summingint (จาน :: getCalories)));
อย่างไรก็ตามนักสะสมอื่นที่มักใช้ร่วมกับการจัดกลุ่มโดยวิธีการทำแผนที่ วิธีนี้ได้รับพารามิเตอร์สองตัว: ฟังก์ชั่นหนึ่งแปลงองค์ประกอบในสตรีมและอื่น ๆ รวบรวมวัตถุผลลัพธ์ที่ถูกแปลง วัตถุประสงค์คือการใช้ฟังก์ชั่นการแมปกับแต่ละองค์ประกอบอินพุตก่อนการสะสมเพื่อให้ตัวสะสมที่ได้รับองค์ประกอบของประเภทใดประเภทหนึ่งสามารถปรับให้เข้ากับวัตถุประเภทต่างๆ ให้ฉันดูตัวอย่างที่ใช้งานได้จริงของการใช้นักสะสมนี้ ตัวอย่างเช่นคุณต้องการได้รับสิ่งที่แคลอรี่มีอยู่ในเมนูสำหรับจานแต่ละประเภท เราสามารถรวมกลุ่มและการทำแผนที่นักสะสมดังนี้:
แผนที่ <type, set <caloriclevel >> caloriclevelsbyType = dishes.stream () .collect (Groupingby (dish :: gettype, การทำแผนที่ (นี่ :: getCaloriclevel, toset ())));
TOSET ที่นี่ใช้ HashSet โดยค่าเริ่มต้นและคุณยังสามารถระบุการใช้งานการใช้งานเฉพาะ (HashSet :: ใหม่) ด้วยตนเองด้วยตนเองด้วยตนเอง
พาร์ทิชัน
การแบ่งพาร์ติชันเป็นกรณีพิเศษของการจัดกลุ่ม: เพรดิเคต (ฟังก์ชั่นที่ส่งคืนค่าบูลีน) ใช้เป็นฟังก์ชันการจำแนกประเภทซึ่งเรียกว่าฟังก์ชันพาร์ติชัน ฟังก์ชั่นพาร์ติชั่นส่งคืนค่าบูลีนซึ่งหมายถึงประเภทคีย์ของแผนที่ที่จัดกลุ่มคือบูลีนดังนั้นจึงสามารถแบ่งออกเป็นสองกลุ่มได้สูงสุด: จริงหรือเท็จ ตัวอย่างเช่นหากคุณเป็นมังสวิรัติคุณอาจต้องการแยกเมนูด้วยมังสวิรัติและไม่ใช่มังสวิรัติ:
แผนที่ <บูลีน, รายการ <dish>> partitionedMenu = dishes.stream (). รวบรวม (partitioningby (dish :: isvegetarian));
แน่นอนว่าการใช้ตัวกรองสามารถบรรลุผลเดียวกันได้:
รายการ <ShASH> มังสวิรัติ DISHES.STREAM (). ตัวกรอง (จาน :: isVetarian) .Collect (collector.tolist ());
ข้อดีของการแบ่งพาร์ติชันคือการบันทึกสองสำเนาซึ่งมีประโยชน์เมื่อคุณต้องการจำแนกรายการ ในเวลาเดียวกันเช่นการจัดกลุ่มโดยพาร์ติชันโดยมีวิธีการมากเกินไปซึ่งสามารถระบุประเภทของค่าการจัดกลุ่ม
แผนที่ <บูลีน, แผนที่ <ประเภท, รายการ <dish>>> มังสวิรัติ DishesByType = DISHES.STREAM (). Collect (Partitioningby (Dish :: Isvegetarian, Groupingby (จาน :: gettype)); แผนที่ <บูลีน Summingint (จาน :: getCalories))); แผนที่ <บูลีน, จาน> MostCaloricPartitionedByVetarian = DISHES.STREAM () .Collect (Partitioningby (Dish :: Isvegetarian, CollectingThen (Maxby (เปรียบเทียบ (จาน:
เป็นตัวอย่างสุดท้ายของการใช้พาร์ติชั่นโดยคอลเลคเตอร์เราได้ใส่โมเดลข้อมูลเมนูไว้เพื่อดูตัวอย่างที่ซับซ้อนและน่าสนใจมากขึ้น: แบ่งอาร์เรย์ออกเป็นตัวเลขที่สำคัญและไม่ใช่ต้นน้ำ
ก่อนอื่นกำหนดฟังก์ชั่นพาร์ติชันที่สำคัญ:
บูลีนส่วนตัว isprime (ผู้สมัคร int) {int candidateroot = (int) math.sqrt ((สอง) ผู้สมัคร); ส่งคืน intstream.rangeclosed (2, Candidateroot) .NonEmatch (i -> ผู้สมัคร % i == 0);}จากนั้นค้นหาหมายเลขนายกรัฐมนตรีและไม่ใช่ต้นน้ำตั้งแต่ 1 ถึง 100
แผนที่ <บูลีน, รายการ <จำนวนเต็ม >> พาร์ติชั่น = intstream.rangeclosed (2, 100) .boxed () .collect (พาร์ติชั่นโดย (นี่ :: isprime));