Java Streams ได้รับการแนะนำมาตลอดทางใน Java 8 ในปี 2014 ด้วยความพยายามที่จะแนะนำ verbose Java ให้กับกระบวนทัศน์การเขียนโปรแกรมเชิงฟังก์ชัน Java Streams แสดงการทำงานที่ยืดหยุ่นและทรงพลังมากมายเพื่อดำเนินการประมวลผลคอลเลกชันในหนึ่งซับ
การกรองคอลเล็กชันตามเพรดิเคตยังคงเป็นหนึ่งในการดำเนินการตามหน้าที่ที่ใช้บ่อยที่สุด และสามารถทำได้ด้วย Predicate
หรืออย่างรัดกุมมากขึ้น - ด้วย a การแสดงออกของแลมบ์ดา.
ในคำแนะนำสั้นๆ นี้ เราจะมาดูกันว่าคุณจะกรองสตรีม Java 8 ด้วย Lambda Expressions ได้อย่างไร
การกรองสตรีมใน Java
โดยทั่วไป ใดๆ Stream
สามารถคัดกรองได้ทาง filter()
วิธีการและภาคแสดงที่กำหนด:
Stream filter(Predicate<? super T> predicate)
แต่ละอิลิเมนต์ในสตรีมจะรันกับเพรดิเคต และจะถูกเพิ่มในสตรีมเอาท์พุตหากเพรดิเคตส่งกลับ true
. คุณสามารถจัดหา Predicate
ตัวอย่าง:
Predicate contains = s -> s.contains("_deprecated");
List results = stream.filter(contains).collect(Collectors.toList());
หรือทำให้ง่ายขึ้นด้วยการจัดเตรียม Lambda Expression:
List results = stream.filter(s -> s.contains("_deprecated"))
.collect(Collectors.toList());
หรือแม้กระทั่งยุบ Lambda Expression เป็น a การอ้างอิงวิธีการ:
List results = stream.filter(String::isEmpty)
.collect(Collectors.toList());
ด้วยการอ้างอิงเมธอด คุณจะไม่สามารถส่งผ่านอาร์กิวเมนต์ได้ อย่างไรก็ตาม คุณสามารถกำหนดเมธอดในวัตถุที่คุณกำลังกรองและปรับแต่งให้กรองได้ง่าย (ตราบใดที่เมธอดไม่ยอมรับอาร์กิวเมนต์และส่งคืน boolean
).
โปรดจำไว้ว่า สตรีมไม่ใช่ของสะสม - พวกเขากำลังสตรีม ของคอลเลกชันและคุณจะต้องรวบรวมกลับเข้าไปในคอลเล็กชันใดๆ เช่น List
, Map
ฯลฯ เพื่อให้มีความคงทนถาวร นอกจากนี้ การดำเนินการทั้งหมดที่ทำกับองค์ประกอบสตรีมก็เช่นกัน สื่อกลาง or สถานีปลายทาง:
- การดำเนินการระดับกลางจะส่งคืนสตรีมใหม่ที่มีการเปลี่ยนแปลงจากการดำเนินการก่อนหน้า
- การดำเนินการของเทอร์มินัลส่งคืนประเภทข้อมูลและมีวัตถุประสงค์เพื่อสิ้นสุดขั้นตอนการประมวลผลบนสตรีม
filter()
เป็น สื่อกลาง การดำเนินการและหมายถึงการเชื่อมโยงกับการดำเนินการขั้นกลางอื่น ๆ ก่อนที่สตรีมจะสิ้นสุดลง หากต้องการคงการเปลี่ยนแปลงใดๆ (เช่น การเปลี่ยนแปลงองค์ประกอบเอง หรือผลลัพธ์ที่กรอง) คุณจะต้องกำหนดผลลัพธ์ กระแสเอาต์พุต ไปยังตัวแปรอ้างอิงใหม่ผ่านการทำงานของเทอร์มินัล
หมายเหตุ แม้เมื่อผูกนิพจน์แลมบ์ดาจำนวนมาก คุณอาจไม่พบปัญหาในการอ่าน ด้วยการแบ่งบรรทัดที่เหมาะสม
ในตัวอย่างต่อไปนี้ เราจะดำเนินการกับรายการหนังสือนี้:
Book book1 = new Book("001", "Our Mathematical Universe", "Max Tegmark", 432, 2014);
Book book2 = new Book("002", "Life 3.0", "Max Tegmark", 280, 2017);
Book book3 = new Book("003", "Sapiens", "Yuval Noah Harari", 443, 2011);
List books = Arrays.asList(book1, book2, book3);
การรวบรวมตัวกรองด้วย Stream.filter()
มากรองหนังสือชุดนี้กัน เพรดิเคตอะไรก็ได้ เช่น กรองตามหนังสือที่มีมากกว่า 400 หน้า:
List results = books.stream()
.filter(b -> b.getPageNumber() > 400)
.collect(Collectors.toList());
ส่งผลให้รายการประกอบด้วย:
[
Book{id='001', name='Our Mathematical Universe', author='Max Tegmark', pageNumber=432, publishedYear=2014},
Book{id='003', name='Sapiens', author='Yuval Noah Harari', pageNumber=443, publishedYear=2011}
]
เมื่อทำการกรอง วิธีที่มีประโยชน์มากในการโยงคือ map()
ซึ่งช่วยให้คุณแมปวัตถุกับค่าอื่นได้ ตัวอย่างเช่น เราสามารถแมปหนังสือแต่ละเล่มกับชื่อของมัน และส่งกลับเฉพาะ the ชื่อ ของหนังสือที่เหมาะกับภาคแสดงจาก filter()
โทร:
List results = books.stream()
.filter(b -> b.getPageNumber() > 400)
.map(Book::getName)
.collect(Collectors.toList());
ส่งผลให้รายการสตริง:
[Our Mathematical Universe, Sapiens]
การรวบรวมตัวกรองในหลายภาคแสดงด้วย Stream.filter()
โดยทั่วไป เราต้องการกรองคอลเลกชันตามเกณฑ์มากกว่าหนึ่งเกณฑ์ ซึ่งสามารถทำได้โดยการผูกมัดหลายๆ filter()
โทร or โดยใช้เพรดิเคตลัดวงจรซึ่งจะตรวจสอบสองเงื่อนไขในหนึ่งเดียว filter()
โทร.
List results = books.stream()
.filter(b -> b.getPageNumber() > 400 && b.getName().length() > 10)
.collect(Collectors.toList());
List results2 = books.stream()
.filter(b -> b.getPageNumber() > 400)
.filter(b -> b.getName().length() > 10)
.collect(Collectors.toList());
ดูคู่มือเชิงปฏิบัติสำหรับการเรียนรู้ Git ที่มีแนวทางปฏิบัติที่ดีที่สุด มาตรฐานที่ยอมรับในอุตสาหกรรม และเอกสารสรุปรวม หยุดคำสั่ง Googling Git และจริงๆ แล้ว เรียน มัน!
เมื่อใช้หลายเกณฑ์ การเรียกใช้แลมบ์ดาอาจใช้เวลานานพอสมควร ณ จุดนี้ การแยกเป็นเพรดิเคตแบบสแตนด์อโลนอาจให้ความชัดเจนมากขึ้น แม้ว่าวิธีการใดจะเร็วกว่า
ตัวกรองเดี่ยวที่มีเงื่อนไขซับซ้อนหรือตัวกรองหลายตัว
ขึ้นอยู่กับฮาร์ดแวร์ของคุณ คอลเล็กชันของคุณมีขนาดใหญ่เพียงใด และคุณใช้สตรีมแบบขนานหรือไม่ โดยทั่วไป – ตัวกรองเดียวที่มีเงื่อนไขซับซ้อนจะมีประสิทธิภาพดีกว่าตัวกรองหลายตัวที่มีเงื่อนไขง่ายกว่า (คอลเล็กชันขนาดเล็กถึงขนาดกลาง) หรือทำงานในระดับเดียวกัน (คอลเล็กชันขนาดใหญ่มาก) หากเงื่อนไขของคุณยาวเกินไป – คุณอาจได้ประโยชน์จากการแจกจ่ายหลายเงื่อนไข filter()
โทรเพื่อให้อ่านง่ายขึ้นเนื่องจากประสิทธิภาพใกล้เคียงกันมาก
ทางเลือกที่ดีที่สุดคือลองทั้งสองอย่าง สังเกตประสิทธิภาพของ อุปกรณ์เป้าหมายและปรับกลยุทธ์ของคุณให้เหมาะสม
ผู้ใช้ GitHub โฟล์คสวาเกน ทำเกณฑ์มาตรฐานการกรองในการดำเนินการปริมาณงาน และโฮสต์ผลลัพธ์บน “javafilters-มาตรฐาน” ที่เก็บ ผลลัพธ์สรุปไว้ในตารางข้อมูล:
มันแสดงให้เห็นการลดลงอย่างชัดเจนของผลตอบแทนที่ขนาดคอลเลกชันที่ใหญ่ขึ้น โดยทั้งสองแนวทางมีประสิทธิภาพใกล้เคียงกัน การสตรีมแบบขนานให้ประโยชน์อย่างมากเมื่อขนาดคอลเลกชันใหญ่ขึ้น แต่ลดประสิทธิภาพลงที่ขนาดที่เล็กกว่า (ต่ำกว่า ~10k องค์ประกอบ) เป็นที่น่าสังเกตว่าสตรีมคู่ขนานรักษาทรูพุตได้ดีกว่าสตรีมที่ไม่ขนานกันมาก ทำให้อินพุตมีประสิทธิภาพมากขึ้น