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 ของเรา