คำแนะนำเกี่ยวกับ Java 8 forEach

1. ภาพรวม

นำมาใช้ใน Java 8, forEachห่วงให้โปรแกรมเมอร์กับใหม่กระชับและวิธีที่น่าสนใจสำหรับวนกว่าคอลเลกชัน

ในบทความนี้เราจะดูวิธีใช้forEachกับคอลเลกชันประเภทของอาร์กิวเมนต์ที่ต้องใช้และการวนซ้ำนี้แตกต่างจากfor-loop ที่ปรับปรุงอย่างไร

หากคุณต้องการทบทวนแนวคิดบางอย่างของ Java 8 เรามีบทความมากมายที่สามารถช่วยคุณได้

2. พื้นฐานของforEach

ใน Java อินเทอร์เฟซCollectionมีIterableเป็น super interface และเริ่มต้นด้วย Java 8 อินเทอร์เฟซนี้มี API ใหม่

void forEach(Consumer action)

พูดง่ายๆก็คือ Javadoc ของforEach stats ที่"ดำเนินการตามที่กำหนดสำหรับแต่ละองค์ประกอบของ Iterable จนกว่าองค์ประกอบทั้งหมดจะได้รับการประมวลผลหรือการกระทำนั้นเกิดข้อยกเว้น"

ดังนั้นด้วยforEachเราสามารถวนซ้ำคอลเลกชันและดำเนินการตามที่กำหนดในแต่ละองค์ประกอบได้เช่นเดียวกับIteratorอื่น ๆ

ตัวอย่างเช่นเวอร์ชันfor-loopของการวนซ้ำและการพิมพ์Collection of Strings :

for (String name : names) { System.out.println(name); }

เราสามารถเขียนสิ่งนี้โดยใช้forEachเป็น:

names.forEach(name -> { System.out.println(name); });

3. ใช้forEach Method

เราใช้forEachเพื่อวนซ้ำคอลเลกชันและดำเนินการบางอย่างกับแต่ละองค์ประกอบ การดำเนินการที่จะดำเนินการมีอยู่ในคลาสที่ใช้อินเทอร์เฟซConsumerและส่งผ่านไปยังforEachเป็นอาร์กิวเมนต์

ผู้บริโภคอินเตอร์เฟซที่เป็นอินเตอร์เฟซที่ใช้งานได้ (อินเตอร์เฟซที่มีวิธีนามธรรมเดี่ยว) ยอมรับอินพุตและไม่ส่งคืนผลลัพธ์

นี่คือคำจำกัดความ:

@FunctionalInterface public interface Consumer { void accept(T t); }

ดังนั้นการใช้งานใด ๆ เช่นผู้บริโภคที่พิมพ์สตริง :

Consumer printConsumer = new Consumer() { public void accept(String name) { System.out.println(name); }; };

สามารถส่งผ่านไปยังforEachเป็นอาร์กิวเมนต์:

names.forEach(printConsumer);

แต่นั่นไม่ใช่วิธีเดียวในการสร้างการดำเนินการผ่านผู้บริโภคและการใช้forEach API

มาดูวิธียอดนิยม 3 วิธีที่เราจะใช้forEach method:

3.1. การใช้งานของผู้บริโภคที่ไม่ระบุตัวตน

เราสามารถสร้างอินสแตนซ์การใช้งานอินเทอร์เฟซConsumerโดยใช้คลาสที่ไม่ระบุชื่อจากนั้นใช้เป็นอาร์กิวเมนต์สำหรับวิธีการforEach :

Consumer printConsumer= new Consumer() { public void accept(String name) { System.out.println(name); } }; names.forEach(printConsumer);

นี้ทำงานได้ดี แต่ถ้าเราวิเคราะห์ตัวอย่างข้างต้นเราจะเห็นว่าส่วนที่เกิดขึ้นจริงที่เป็นของใช้เป็นรหัสภายในยอมรับ ()วิธีการ

แม้ว่านิพจน์ Lambda จะเป็นบรรทัดฐานและเป็นวิธีที่ง่ายกว่าในการทำเช่นนี้ แต่ก็ยังควรทราบวิธีใช้อินเทอร์เฟซสำหรับผู้บริโภค

3.2. นิพจน์แลมด้า

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

เนื่องจากConsumer Interface เป็นอินเทอร์เฟซที่ใช้งานได้เราสามารถแสดงมันใน Lambda ในรูปแบบของ:

(argument) -> { //body }

ดังนั้นprintConsumerของเราจึงง่ายต่อการ:

name -> System.out.println(name)

และเราสามารถส่งต่อไปยังforEach :

names.forEach(name -> System.out.println(name));

นับตั้งแต่มีการนำนิพจน์แลมด้ามาใช้ใน Java 8 นี่อาจเป็นวิธีที่ใช้บ่อยที่สุดในการใช้เมธอดforEach

Lambdas มีเส้นโค้งการเรียนรู้ที่แท้จริงดังนั้นหากคุณเริ่มต้นบทความนี้จะกล่าวถึงแนวทางปฏิบัติที่ดีในการใช้งานคุณลักษณะภาษาใหม่

3.3. การอ้างอิงวิธีการ

เราสามารถใช้วิธีการอ้างอิงไวยากรณ์แทนไวยากรณ์ Lambda ปกติที่มีวิธีการอยู่แล้วเพื่อดำเนินการกับคลาส:

names.forEach(System.out::println);

4. การทำงานกับforEach

4.1. วนซ้ำคอลเลกชัน

คอลเลกชันประเภทที่ทำซ้ำได้- รายการชุดคิวและอื่น ๆ มีไวยากรณ์เดียวกันสำหรับใช้forEach

ดังนั้นดังที่เราได้เห็นไปแล้วการวนซ้ำองค์ประกอบของรายการ:

List names = Arrays.asList("Larry", "Steve", "James"); names.forEach(System.out::println);

ในทำนองเดียวกันสำหรับชุด:

Set uniqueNames = new HashSet(Arrays.asList("Larry", "Steve", "James")); uniqueNames.forEach(System.out::println);

หรือสมมติว่าเป็นคิวซึ่งเป็นคอลเล็กชันด้วย :

Queue namesQueue = new ArrayDeque(Arrays.asList("Larry", "Steve", "James")); namesQueue.forEach(System.out::println);

4.2. iterating กว่าแผนที่ - การใช้แผนที่ของforEach

แผนที่ไม่ได้Iterableแต่พวกเขาไม่ให้ความแตกต่างของตัวเองforEachที่ยอมรับBiConsumer

BiConsumerถูกนำมาใช้แทนของผู้บริโภคใน Iterable ของforEachเพื่อให้การดำเนินการสามารถดำเนินการได้ทั้งที่สำคัญและคุณค่าของการมีแผนที่พร้อมกัน

มาสร้างแผนที่ที่มีรายการ:

Map namesMap = new HashMap(); namesMap.put(1, "Larry"); namesMap.put(2, "Steve"); namesMap.put(3, "James");

ถัดไปขอย้ำกว่าnamesMapใช้แผนที่ของforEach :

namesMap.forEach((key, value) -> System.out.println(key + " " + value));

ดังที่เราเห็นที่นี่เราใช้BiConsumer :

(key, value) -> System.out.println(key + " " + value)

ย้ำกว่ารายการของแผนที่

4.3. การทำซ้ำบนแผนที่ -โดยการทำซ้ำentrySet

นอกจากนี้เรายังสามารถทำซ้ำEntrySetของแผนที่โดยใช้ Iterable's forEach

เนื่องจากรายการของแผนที่ถูกเก็บไว้ในชุดที่เรียกว่าEntrySetเราจึงสามารถทำซ้ำได้โดยใช้forEach:

namesMap.entrySet().forEach(entry -> System.out.println( entry.getKey() + " " + entry.getValue()));

5. Foreach vs For-Loop

จากมุมมองที่เรียบง่ายลูปทั้งสองมีฟังก์ชันการทำงานที่เหมือนกัน - วนซ้ำองค์ประกอบในคอลเลกชัน

ความแตกต่างที่สำคัญระหว่างสองของพวกเขาที่พวกเขามี iterators แตกต่างกัน - ขั้นสูงสำหรับวงเป็น iterator ภายนอกในขณะที่ใหม่forEachวิธีการเป็นหนึ่งภายใน

5.1. Internal Iterator - forEach

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

ตัววนซ้ำจะจัดการการวนซ้ำแทนและตรวจสอบให้แน่ใจว่าได้ประมวลผลองค์ประกอบทีละรายการ

มาดูตัวอย่างของตัววนซ้ำภายใน:

names.forEach(name -> System.out.println(name));

ในforEachวิธีการด้านบนเราจะเห็นว่าอาร์กิวเมนต์ที่ให้มาเป็นนิพจน์แลมบ์ดา ซึ่งหมายความว่าวิธีนี้จำเป็นต้องรู้ว่าจะต้องทำอะไรเท่านั้นและงานทั้งหมดของการทำซ้ำจะได้รับการดูแลจากภายใน

5.2. External Iterator - for-loop

ตัววนซ้ำภายนอกจะผสมผสานสิ่งที่ต้องทำและวิธีการวนซ้ำ

Enumerations , Iteratorsและ Enhanced for-loopเป็นตัววนซ้ำภายนอกทั้งหมด (จำ method iterator (), next ()หรือhasNext () ?) ในการวนซ้ำทั้งหมดนี้เป็นหน้าที่ของเราในการระบุวิธีดำเนินการซ้ำ

พิจารณาลูปที่คุ้นเคยนี้:

for (String name : names) { System.out.println(name); }

แม้ว่าเราจะไม่ได้เรียกใช้เมธอด hasNext ()หรือnext ()อย่างชัดเจนในขณะที่ทำซ้ำในรายการ แต่โค้ดที่ใช้ในการทำซ้ำนี้จะใช้วิธีการเหล่านี้ นี่หมายความว่าความซับซ้อนของการดำเนินการเหล่านี้ถูกซ่อนจากโปรแกรมเมอร์ แต่ก็ยังคงมีอยู่

ตรงกันข้ามกับตัววนซ้ำภายในที่คอลเลคชันทำการวนซ้ำเองที่นี่เราต้องการโค้ดภายนอกที่นำทุกองค์ประกอบออกจากคอลเล็กชัน

6. บทสรุป

ในบทความนี้เราแสดงให้เห็นว่าforEach loop นั้นสะดวกกว่าfor-loop แบบปกติ

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

ในที่สุดตัวอย่างทั้งหมดที่ใช้ในบทความนี้มีอยู่ในที่เก็บ Github ของเรา