JavaRush /จาวาบล็อก /Random-TH /For และ For-Each Loop: เรื่องราวเกี่ยวกับวิธีที่ฉันวนซ้ำ ...
Viacheslav
ระดับ

For และ For-Each Loop: เรื่องราวเกี่ยวกับวิธีที่ฉันวนซ้ำ ถูกวนซ้ำ แต่ไม่ได้วนซ้ำ

เผยแพร่ในกลุ่ม

การแนะนำ

ลูปเป็นหนึ่งในโครงสร้างพื้นฐานของภาษาการเขียนโปรแกรม ตัวอย่างเช่น บนเว็บไซต์ Oracle มีส่วน " บทเรียน: พื้นฐานภาษา " ซึ่งในลูปจะมีบทเรียนแยกต่างหาก " The for Statement " มารีเฟรชพื้นฐานกันดีกว่า: การวนซ้ำประกอบด้วยสามนิพจน์ (คำสั่ง): การเริ่มต้น (การเริ่มต้น) เงื่อนไข (การสิ้นสุด) และการเพิ่มขึ้น (การเพิ่มขึ้น):
For และ For-Each Loop: เรื่องราวเกี่ยวกับวิธีที่ฉันวนซ้ำ วนซ้ำ แต่ไม่ได้วนซ้ำ - 1
สิ่งที่น่าสนใจคือทั้งหมดนี้เป็นทางเลือก ซึ่งหมายความว่าเราสามารถเขียนได้หากต้องการว่า:
for (;;){
}
จริงอยู่ ในกรณีนี้ เราจะได้วนซ้ำไม่สิ้นสุดเพราะว่า เราไม่ได้ระบุเงื่อนไขในการออกจากลูป (การสิ้นสุด) นิพจน์การเริ่มต้นจะดำเนินการเพียงครั้งเดียว ก่อนที่จะดำเนินการวนซ้ำทั้งหมด ควรจำไว้เสมอว่าวงจรมีขอบเขตของมันเอง ซึ่งหมายความว่าการกำหนดค่าเริ่มต้น การยกเลิกการเพิ่มขึ้นและเนื้อหาลูปจะเห็นตัวแปรเดียวกัน กำหนดขอบเขตได้ง่ายเสมอโดยใช้เครื่องหมายปีกกา ทุกสิ่งที่อยู่ในวงเล็บจะไม่สามารถมองเห็นได้ภายนอกวงเล็บ แต่ทุกสิ่งที่อยู่นอกวงเล็บจะมองเห็นได้ภายในวงเล็บ การเริ่มต้นเป็นเพียงการแสดงออก ตัวอย่างเช่น แทนที่จะเริ่มต้นตัวแปร คุณสามารถเรียกใช้เมธอดที่จะไม่ส่งคืนสิ่งใดๆ ได้ หรือข้ามไป โดยเว้นช่องว่างก่อนเครื่องหมายอัฒภาคแรก นิพจน์ต่อไปนี้ระบุเงื่อนไขการสิ้นสุด ตราบใดที่เป็นจริงการวนซ้ำก็จะถูกดำเนินการ และหากเป็นfalseการวนซ้ำใหม่จะไม่เริ่มต้นขึ้น หากคุณดูภาพด้านล่าง เราได้รับข้อผิดพลาดระหว่างการคอมไพล์ และ IDE จะบ่น: ไม่สามารถเข้าถึงนิพจน์ของเราในลูปได้ เนื่องจากเราจะไม่มีการวนซ้ำเพียงครั้งเดียวในลูป เราจะออกทันทีเพราะว่า เท็จ:
For และ For-Each Loop: เรื่องราวเกี่ยวกับวิธีที่ฉันวนซ้ำ วนซ้ำ แต่ไม่ได้วนซ้ำ - 2
ควรจับตาดู นิพจน์ในคำสั่งยุติเนื่องจากจะกำหนดโดยตรงว่าแอปพลิเคชันของคุณจะมีการวนซ้ำไม่สิ้นสุดหรือไม่ การเพิ่มขึ้นเป็นนิพจน์ที่ง่ายที่สุด มันถูกดำเนินการหลังจากการวนซ้ำสำเร็จแต่ละครั้ง และสำนวนนี้ก็สามารถข้ามได้ ตัวอย่างเช่น:
int outerVar = 0;
for (;outerVar < 10;) {
	outerVar += 2;
	System.out.println("Value = " + outerVar);
}
ดังที่คุณเห็นจากตัวอย่าง การวนซ้ำแต่ละครั้งเราจะเพิ่มทีละ 2 แต่ตราบเท่าที่ค่าouterVarน้อยกว่า 10 เท่านั้น นอกจากนี้ เนื่องจากนิพจน์ในคำสั่งส่วนเพิ่มนั้นแท้จริงแล้วเป็นเพียงนิพจน์ ดังนั้น สามารถมีอะไรก็ได้ ดังนั้นจึงไม่มีใครห้ามการใช้การลดลงแทนการเพิ่มขึ้น กล่าวคือ ลดมูลค่า คุณควรติดตามการเขียนส่วนเพิ่มเสมอ +=ดำเนินการเพิ่มก่อนแล้วจึงมอบหมายงาน แต่ถ้าในตัวอย่างด้านบนเราเขียนตรงกันข้าม เราจะได้วงวนไม่สิ้นสุด เนื่องจากตัวแปรouterVarจะไม่มีวันได้รับค่าที่เปลี่ยนแปลง: ในกรณีนี้ ตัวแปร=+จะถูกคำนวณหลังจากการมอบหมาย อย่างไรก็ตาม การเพิ่มจำนวนการดูก็เหมือน++กัน ตัวอย่างเช่น เรามีการวนซ้ำ:
String[] names = {"John","Sara","Jack"};
for (int i = 0; i < names.length; ++i) {
	System.out.println(names[i]);
}
วงจรใช้งานได้และไม่มีปัญหา แต่แล้วคนปรับโครงสร้างก็มา เขาไม่เข้าใจการเพิ่มขึ้นและทำสิ่งนี้:
String[] names = {"John","Sara","Jack"};
for (int i = 0; i < names.length;) {
	System.out.println(names[++i]);
}
หากเครื่องหมายเพิ่มขึ้นปรากฏด้านหน้าค่า หมายความว่าจะเพิ่มขึ้นก่อนแล้วจึงกลับไปยังตำแหน่งที่ระบุไว้ ในตัวอย่างนี้ เราจะเริ่มแยกองค์ประกอบที่ดัชนี 1 ออกจากอาร์เรย์ทันที โดยข้ามองค์ประกอบแรก จากนั้นที่ดัชนี 3 เราจะเกิดข้อผิดพลาด " java.lang.ArrayIndexOutOfBoundsException " ดังที่คุณอาจเดาได้ สิ่งนี้ได้ผลก่อนหน้านี้เพียงเพราะการเพิ่มขึ้นถูกเรียกหลังจากการวนซ้ำเสร็จสิ้น เมื่อถ่ายโอนนิพจน์นี้ไปสู่การวนซ้ำ ทุกอย่างพัง ปรากฎว่าแม้แต่ในวงธรรมดา ๆ คุณก็สามารถสร้างความยุ่งเหยิงได้) หากคุณมีอาร์เรย์ บางทีอาจมีวิธีที่ง่ายกว่าในการแสดงองค์ประกอบทั้งหมดหรือไม่
For และ For-Each Loop: เรื่องราวเกี่ยวกับวิธีที่ฉันวนซ้ำ วนซ้ำ แต่ไม่ได้วนซ้ำ - 3

สำหรับแต่ละวง

เริ่มต้นด้วย Java 1.5 นักพัฒนา Java ได้ให้การออกแบบfor each loopที่อธิบายไว้ในไซต์ Oracle ในคำแนะนำชื่อ " The For-Each Loop " หรือสำหรับเวอร์ชัน1.5.0แก่ เรา โดยทั่วไปจะมีลักษณะดังนี้:
For และ For-Each Loop: เรื่องราวเกี่ยวกับวิธีที่ฉันวนซ้ำ วนซ้ำ แต่ไม่ได้วนซ้ำ - 4
คุณสามารถอ่านคำอธิบายของโครงสร้างนี้ได้ใน Java Language Specification (JLS) เพื่อให้แน่ใจว่าไม่ใช่สิ่งมหัศจรรย์ โครงสร้างนี้อธิบายไว้ในบท " 14.14.2. การปรับปรุงสำหรับคำสั่ง " อย่างที่คุณเห็นfor แต่ละวงสามารถใช้กับอาร์เรย์และอาร์เรย์ที่ใช้java.lang.Iterable interface นั่นคือถ้าคุณต้องการจริงๆ คุณสามารถใช้ อินเทอร์เฟซ java.lang.Iterableและสำหรับแต่ละลูปสามารถใช้กับคลาสของคุณได้ คุณจะพูดทันทีว่า “โอเค มันเป็นวัตถุที่สามารถทำซ้ำได้ แต่อาร์เรย์ไม่ใช่วัตถุ เรียงลำดับของ” แล้วคุณจะคิดผิดเพราะ... ใน Java อาร์เรย์เป็นวัตถุที่สร้างขึ้นแบบไดนามิก ข้อกำหนดภาษาบอกเราสิ่งนี้: “ ในภาษาการเขียนโปรแกรม Java อาร์เรย์คือวัตถุ ” โดยทั่วไปแล้ว อาร์เรย์เป็นเหมือนเวทมนตร์ JVM เล็กน้อย เพราะ... ไม่ทราบโครงสร้างอาร์เรย์ภายในอย่างไรและอยู่ที่ไหนสักแห่งภายใน Java Virtual Machine ใครที่สนใจสามารถอ่านคำตอบเกี่ยวกับ stackoverflow: " คลาสอาร์เรย์ทำงานอย่างไรใน Java " ปรากฎว่าถ้า เราไม่ได้ใช้อาร์เรย์ เราก็ต้องใช้บางอย่างที่ Implement Iterable ตัวอย่างเช่น:
List<String> names = Arrays.asList("John", "Sara", "Jack");
for (String name : names) {
	System.out.println("Name = " + name);
}
ที่นี่คุณคงจำได้ว่าถ้าเราใช้คอลเลกชัน ( java.util.Collection ) ด้วยเหตุนี้เราจึงได้รับIterable อย่าง แน่นอน หากวัตถุมีคลาสที่ใช้ Iterable ก็จำเป็นต้องจัดเตรียม Iterator ที่จะวนซ้ำเนื้อหาของวัตถุนั้นเมื่อมีการเรียกใช้เมธอดตัววนซ้ำ ตัวอย่างเช่นโค้ดด้านบนจะมีโค้ดไบต์ประมาณนี้ (ใน IntelliJ Idea คุณสามารถ "View" -> "Show bytecode" :
For และ For-Each Loop: เรื่องราวเกี่ยวกับวิธีที่ฉันวนซ้ำ วนซ้ำ แต่ไม่ได้วนซ้ำ - 5
อย่างที่คุณเห็น มีการใช้ตัววนซ้ำจริงๆ หากไม่ใช่สำหรับ for แต่ละ loopเราจะต้องเขียนดังนี้:
List<String> names = Arrays.asList("John", "Sara", "Jack");
for (Iterator i = names.iterator(); /* continue if */ i.hasNext(); /* skip increment */) {
	String name = (String) i.next();
	System.out.println("Name = " + name);
}

ตัววนซ้ำ

ดังที่เราเห็นข้างต้น อินเทอร์เฟซ Iterableบอกว่าสำหรับอินสแตนซ์ของวัตถุบางอย่าง คุณสามารถรับตัววนซ้ำซึ่งคุณสามารถวนซ้ำเนื้อหาได้ อาจกล่าวได้ ว่าเป็น Single Responsibility Principle จากSOLID อีกครั้ง โครงสร้างข้อมูลไม่ควรขับเคลื่อนการข้ามผ่าน แต่สามารถจัดเตรียมสิ่งที่ควรได้ การใช้งานพื้นฐานของ Iteratorคือโดยปกติแล้วจะประกาศให้เป็นคลาสภายในที่สามารถเข้าถึงเนื้อหาของคลาสภายนอกและจัดเตรียมองค์ประกอบที่ต้องการที่มีอยู่ในคลาสภายนอก นี่คือตัวอย่างจากคลาสArrayListของวิธีที่ตัววนซ้ำส่งคืนองค์ประกอบ:
public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
}
ดังที่เราเห็น ด้วยความช่วยเหลือของArrayList.thisตัววนซ้ำจะเข้าถึงคลาสภายนอกและตัวแปรของมัน จากelementDataนั้นจึงส่งคืนองค์ประกอบจากที่นั่น ดังนั้นการรับตัววนซ้ำจึงง่ายมาก:
List<String> names = Arrays.asList("John", "Sara", "Jack");
Iterator<String> iterator = names.iterator();
งานของมันขึ้นอยู่กับข้อเท็จจริงที่ว่าเราสามารถตรวจสอบได้ว่ามีองค์ประกอบเพิ่มเติมหรือไม่ ( วิธี hasNext ) รับองค์ประกอบถัดไป ( วิธี ถัดไป ) และ วิธี ลบซึ่งจะลบองค์ประกอบสุดท้ายที่ได้รับผ่านnext วิธีการลบเป็นทางเลือกและไม่รับประกันว่าจะนำไปใช้ได้ ในความเป็นจริง เมื่อ Java พัฒนาขึ้น อินเทอร์เฟซก็วิวัฒนาการไปด้วย ดังนั้นใน Java 8 จึงมีวิธีการforEachRemainingที่ช่วยให้คุณสามารถดำเนินการบางอย่างกับองค์ประกอบที่เหลือที่ตัววนซ้ำไม่ได้เยี่ยมชม สิ่งที่น่าสนใจเกี่ยวกับตัววนซ้ำและคอลเลกชัน? ตัวอย่างเช่นมีชั้นAbstractListเรียน นี่คือคลาสนามธรรมที่เป็นพาเรนต์ของArrayListและ และเป็นเรื่องที่น่าสนใจสำหรับ เราLinkedListเนื่องจากมีฟิลด์เช่นmodCount การเปลี่ยนแปลงแต่ละครั้งเนื้อหาของการเปลี่ยนแปลงรายการ แล้วมันสำคัญอะไรกับเราล่ะ? และความจริงที่ว่าตัววนซ้ำทำให้แน่ใจว่าในระหว่างการดำเนินการคอลเลกชันที่มีการวนซ้ำจะไม่เปลี่ยนแปลง ตามที่คุณเข้าใจ การใช้งานตัววนซ้ำสำหรับรายการจะอยู่ในตำแหน่งเดียวกับmodcount นั่นคือในคลาส AbstractListลองดูตัวอย่างง่ายๆ:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Iterator<String> iterator = names.iterator();
names.add("modcount++");
System.out.println(iterator.next());
นี่คือสิ่งแรกที่น่าสนใจแม้ว่าจะไม่อยู่ในหัวข้อก็ตาม จริงๆ แล้วArrays.asListส่งคืนอันพิเศษของตัวเองArrayList( java.util.Arrays.ArrayList ) มันไม่ได้ใช้วิธีการเพิ่ม ดังนั้นจึงไม่สามารถแก้ไขได้ มีเขียนเกี่ยวกับใน JavaDoc: ขนาดคงที่ แต่จริงๆ แล้ว มันเป็นมากกว่าขนาดคงที่ มัน ไม่ เปลี่ยนรูปเช่น กัน นั่นคือไม่เปลี่ยนแปลง ลบจะไม่ทำงานเช่นกัน เราก็จะได้รับข้อผิดพลาดเช่นกันเพราะ... เมื่อสร้างตัววนซ้ำแล้ว เราก็จำ modcountไว้ในนั้นได้ จากนั้นเราเปลี่ยนสถานะของคอลเลกชัน “ภายนอก” (เช่น ไม่ใช่ผ่านตัววนซ้ำ) และดำเนินการเมธอดตัววนซ้ำ ดังนั้นเราจึงได้รับข้อผิดพลาด: java.util.ConcurrentModificationException เพื่อหลีกเลี่ยงปัญหานี้ การเปลี่ยนแปลงระหว่างการวนซ้ำจะต้องดำเนินการผ่านตัววนซ้ำเอง ไม่ใช่ผ่านการเข้าถึงคอลเลกชัน:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Iterator<String> iterator = names.iterator();
iterator.next();
iterator.remove();
System.out.println(iterator.next());
อย่างที่คุณเข้าใจถ้าiterator.remove()คุณไม่ทำมา ก่อน iterator.next()ก็เพราะว่า ตัววนซ้ำไม่ได้ชี้ไปที่องค์ประกอบใด ๆ จากนั้นเราจะได้รับข้อผิดพลาด ในตัวอย่าง ตัววนซ้ำจะไปที่ องค์ประกอบ Johnลบออก และรับองค์ประกอบSara และที่นี่ทุกอย่างจะเรียบร้อยดี แต่โชคร้ายอีกครั้งที่มี "ความแตกต่าง") java.util.ConcurrentModificationExceptionจะเกิดขึ้นก็ต่อเมื่อhasNext()มันส่งกลับtrueเท่านั้น นั่นคือ หากคุณลบองค์ประกอบสุดท้ายผ่านคอลเลกชัน ตัววนซ้ำจะไม่ตก สำหรับรายละเอียดเพิ่มเติม ควรดูรายงานเกี่ยวกับปริศนา Java จาก “ #ITsubbotnik Section JAVA: Java Puzzles ” เราเริ่มการสนทนาโดยละเอียดด้วยเหตุผลง่ายๆ ที่ใช้ความแตกต่างเดียวกันทุกประการเมื่อfor each loop... ตัววนซ้ำที่เราชื่นชอบนั้นถูกใช้ภายใต้ประทุน และความแตกต่างทั้งหมดนี้ก็มีผลเช่นกัน สิ่งเดียวคือ เราจะไม่สามารถเข้าถึงตัววนซ้ำ และเราไม่สามารถลบองค์ประกอบได้อย่างปลอดภัย ตามที่คุณเข้าใจแล้ว สถานะจะถูกจดจำในขณะที่สร้างตัววนซ้ำ และการลบอย่างปลอดภัยจะทำงานเฉพาะเมื่อมีการเรียกเท่านั้น นั่นคือตัวเลือกนี้จะไม่ทำงาน:
Iterator<String> iterator1 = names.iterator();
Iterator<String> iterator2 = names.iterator();
iterator1.next();
iterator1.remove();
System.out.println(iterator2.next());
เนื่องจากสำหรับตัววนซ้ำ 2 การลบผ่านตัววนซ้ำ 1 นั้นเป็น "ภายนอก" นั่นคือมีการดำเนินการที่ไหนสักแห่งภายนอกและเขาไม่รู้อะไรเลย ในหัวข้อตัววนซ้ำ ฉันอยากจะทราบสิ่งนี้ด้วย Listตัววนซ้ำ แบบ ขยายพิเศษถูกสร้างขึ้นมา สำหรับการใช้งานอินเทอร์เฟซโดยเฉพาะ และพวกเขาก็ตั้งชื่อเขาListIteratorว่า ช่วยให้คุณเคลื่อนที่ไม่เพียง แต่ไปข้างหน้า แต่ยังย้อนกลับและยังช่วยให้คุณค้นหาดัชนีขององค์ประกอบก่อนหน้าและองค์ประกอบถัดไป นอกจากนี้ยังช่วยให้คุณสามารถแทนที่องค์ประกอบปัจจุบันหรือแทรกองค์ประกอบใหม่ที่ตำแหน่งระหว่างตำแหน่งตัววนซ้ำปัจจุบันและตำแหน่งถัดไป ตามที่คุณเดาListIteratorอนุญาตให้ทำเช่นนี้ได้เนื่องจากListมีการใช้งานการเข้าถึงโดยดัชนี
For และ For-Each Loop: เรื่องราวเกี่ยวกับวิธีที่ฉันวนซ้ำ วนซ้ำ แต่ไม่ได้วนซ้ำ - 6

Java 8 และการวนซ้ำ

การเปิดตัว Java 8 ทำให้ชีวิตของหลายๆ คนง่ายขึ้น นอกจากนี้เรายังไม่ได้ละเลยการวนซ้ำเนื้อหาของวัตถุ เพื่อให้เข้าใจถึงวิธีการทำงาน คุณต้องพูดสักสองสามคำเกี่ยวกับเรื่องนี้ Java 8 เปิดตัวคลาสjava.util.function.Consumer นี่คือตัวอย่าง:
Consumer consumer = new Consumer() {
	@Override
	public void accept(Object o) {
		System.out.println(o);
	}
};
Consumerเป็นอินเทอร์เฟซที่ใช้งานได้ซึ่งหมายความว่าภายในอินเทอร์เฟซมีเพียง 1 วิธีนามธรรมที่ไม่ได้ใช้งานเท่านั้นที่ต้องมีการใช้งานบังคับในคลาสที่ระบุการใช้งานของอินเทอร์เฟซนี้ สิ่งนี้ทำให้คุณสามารถใช้สิ่งมหัศจรรย์เช่นแลมบ์ดาได้ บทความนี้ไม่เกี่ยวกับเรื่องนั้น แต่เราต้องเข้าใจว่าเหตุใดเราจึงนำไปใช้ได้ ดังนั้น เมื่อใช้ lambdas Consumer ข้างต้น สามารถเขียนใหม่ได้ดังนี้: Consumer consumer = (obj) -> System.out.println(obj); ซึ่งหมายความว่า Java เห็นว่าสิ่งที่เรียกว่า obj จะถูกส่งผ่านไปยังอินพุต จากนั้นนิพจน์หลังจาก -> จะถูกดำเนินการสำหรับ obj นี้ สำหรับการวนซ้ำ ตอนนี้เราสามารถทำได้ดังนี้:
List<String> names = Arrays.asList("John", "Sara", "Jack");
Consumer consumer = (obj) -> System.out.println(obj);
names.forEach(consumer);
หากคุณไปที่วิธีการforEachคุณจะเห็นว่าทุกอย่างเรียบง่ายมาก มีอันโปรดของเราfor-each loop:
default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
}
นอกจากนี้ยังสามารถลบองค์ประกอบได้อย่างสวยงามโดยใช้ตัววนซ้ำ เช่น:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Predicate predicate = (obj) -> obj.equals("John");
names.removeIf(predicate);
ในกรณีนี้ เมธอด RemoveIfจะใช้เป็นอินพุต ไม่ใช่Consumerแต่เป็นเพรดิเคต มันคืนค่าบูลีน ในกรณีนี้ หากภาคแสดงระบุว่า " จริง " องค์ประกอบนั้นจะถูกลบออก เป็นเรื่องน่าสนใจที่ทุกอย่างไม่ชัดเจนที่นี่เช่นกัน)) คุณต้องการอะไร? ประชาชนจำเป็นต้องได้รับพื้นที่เพื่อสร้างปริศนาในการประชุม ตัวอย่างเช่น ลองใช้โค้ดต่อไปนี้เพื่อลบทุกสิ่งที่ตัววนซ้ำสามารถเข้าถึงได้หลังจากการวนซ้ำ:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Iterator<String> iterator = names.iterator();
iterator.next(); // Курсор на John
while (iterator.hasNext()) {
    iterator.next(); // Следующий элемент
    iterator.remove(); // Удалor его
}
System.out.println(names);
โอเค ทุกอย่างใช้งานได้ที่นี่ แต่เราจำได้ว่า Java 8 นั้น ดังนั้นเรามาลองทำให้โค้ดง่ายขึ้น:
List<String> names = Arrays.asList("John", "Sara", "Jack");
names = new ArrayList(names);
Iterator<String> iterator = names.iterator();
iterator.next(); // Курсор на John
iterator.forEachRemaining(obj -> iterator.remove());
System.out.println(names);
สวยขึ้นจริงมั้ย? อย่างไรก็ตาม จะมีjava.lang.IllegalStateException และเหตุผลก็คือ... มีข้อบกพร่องใน Java ปรากฎว่ามันได้รับการแก้ไขแล้ว แต่ใน JDK 9 นี่คือลิงค์ไปยังงานใน OpenJDK: Iterator.forEachRemaining vs. Iterator.remove _ แน่นอนว่าสิ่งนี้ได้ถูกพูดคุยกันแล้ว: เหตุใด iterator.forEachRemaining จึงไม่ลบองค์ประกอบใน Consumer lambda อีกวิธีหนึ่งคือผ่าน Stream API โดยตรง:
List<String> names = new ArrayList(Arrays.asList("John", "Sara", "Jack"));
Stream<String> stream = names.stream();
stream.forEach(obj -> System.out.println(obj));

ข้อสรุป

ดังที่เราเห็นจากเนื้อหาทั้งหมดข้างต้น การวนซ้ำfor-each loopเป็นเพียง "น้ำตาลเชิงวากยสัมพันธ์" ที่ด้านบนของตัววนซ้ำ อย่างไรก็ตาม ปัจจุบันมีการใช้กันในหลายแห่ง นอกจากนี้จะต้องใช้ผลิตภัณฑ์ใด ๆ ด้วยความระมัดระวัง ตัวอย่างเช่น ผู้ที่ไม่เป็นอันตรายforEachRemainingอาจซ่อนความประหลาดใจอันไม่พึงประสงค์ไว้ และนี่เป็นการพิสูจน์อีกครั้งว่าจำเป็นต้องมีการทดสอบหน่วย การทดสอบที่ดีสามารถระบุกรณีการใช้งานดังกล่าวในโค้ดของคุณได้ สิ่งที่คุณสามารถดู/อ่านในหัวข้อ: #เวียเชสลาฟ
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION