1. ความเป็นมาเกี่ยวกับวิธีการวนซ้ำ

คุณคุ้นเคยHashSetกับ หากคุณได้ศึกษามันจริงๆ นอกเหนือจากการอ่านบทเรียนแล้ว คุณควรถามคำถามนี้:

ฉันจะแสดงรายการองค์ประกอบ HashSet ทั้งหมดบนหน้าจอได้อย่างไร ท้ายที่สุดไม่มีอินเทอร์เฟซget()และset()วิธีการ!

และHashSetไม่ได้อยู่คนเดียวในข้อจำกัดนี้ นอกจากนี้ ยังHashSetมีคอลเล็กชันอื่นๆ อีกมากมายที่ไม่อนุญาตให้ดึงองค์ประกอบโดยดัชนี เนื่องจากองค์ประกอบไม่มีลำดับที่กำหนดไว้

ในช่วงหลายปีที่ผ่านมา โปรแกรมเมอร์ได้คิดค้นโครงสร้างข้อมูลที่ซับซ้อนมากมาย เช่น กราฟและต้นไม้ หรือรายการของรายการ.

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

และเรายังได้รับสถานการณ์ที่มีคอนเทนเนอร์ที่เก็บองค์ประกอบ แต่ไม่ได้อยู่ในลำดับที่ตายตัว

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


2. Iterator สำหรับคอลเลกชัน

Iteratorsถูกเสนอเป็นวิธีแก้ปัญหาข้างต้น

ตัววนซ้ำเป็นวัตถุพิเศษที่เกี่ยวข้องกับคอลเลกชัน ซึ่งช่วยสำรวจองค์ประกอบทั้งหมดของคอลเลกชันโดยไม่ต้องทำซ้ำ

คุณสามารถใช้รหัสต่อไปนี้เพื่อรับตัววนซ้ำสำหรับคอลเลกชันใดๆ:

Iterator<Type> it = name.iterator();

โดยที่nameชื่อของตัวแปรคอลเล็กชันTypeคือประเภทขององค์ประกอบของคอลเล็กชันiterator()เป็นหนึ่งในเมธอดของคอลเล็กชัน และitเป็นชื่อของตัวแปรวนซ้ำ

ตัววนซ้ำมี 3 วิธี:

วิธี คำอธิบาย
Type next()
ส่งกลับองค์ประกอบถัดไปในคอลเลกชัน
boolean hasNext()
ตรวจสอบว่ามีองค์ประกอบใดบ้างที่ยังไม่ได้สำรวจ
void remove()
ลบองค์ประกอบปัจจุบันของคอลเลกชัน

เมธอดเหล่านี้ค่อนข้างคล้ายกับคลาสnextInt)และhasNextInt()วิธีการ ของ Scanner

เมธอดnext()ส่งคืนองค์ประกอบถัดไปของคอลเลกชันที่เราได้รับตัววนซ้ำ

เมธอดhasNext()ตรวจสอบว่าคอลเล็กชันมีองค์ประกอบเพิ่มเติมที่ตัววนซ้ำยังไม่ส่งคืนหรือไม่

นี่คือวิธีแสดงองค์ประกอบทั้งหมดของ a HashSet:

รหัส หมายเหตุ
HashSet<String> set = new HashSet<String>();

set.add("Hello");
set.add("Hello");
set.add("Hola");
set.add("Bonjour");
set.add("Ciao");
set.add("Namaste");

Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
สร้างHashSetวัตถุที่เก็บStringองค์ประกอบ


เราเพิ่มคำทักทายในภาษาต่างๆ ให้กับsetตัวแปร




รับตัววนซ้ำวัตถุสำหรับsetชุด
ตราบใดที่ยังมีองค์ประกอบ

รับองค์ประกอบถัดไป
แสดงองค์ประกอบบนหน้าจอ


3. For-eachวนซ้ำ

ข้อเสียเปรียบหลักของตัววนซ้ำคือโค้ดของคุณจะยุ่งยากกว่าการใช้forลูป

ในการเปรียบเทียบ ลองแสดงรายการโดยใช้forการวนซ้ำและใช้ตัววนซ้ำ:

วนซ้ำ สำหรับลูป
ArrayList<String> list = new ArrayList<String>();

Iterator<String> it = list.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}

ใช่ มันจะดีกว่ามากที่จะสำรวจองค์ประกอบของArrayListการใช้ลูป — ทุกอย่างจะสั้นลง

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

พวกเขาสร้างลูปชนิดใหม่ให้กับ Java และเรียกมันว่าfor-eachลูป นี่คือลักษณะโดยทั่วไป:

for(Type name:collection)

โดยที่collectionชื่อของตัวแปรคอลเล็กชันTypeคือประเภทขององค์ประกอบในคอลเล็กชัน และnameเป็นชื่อของตัวแปรที่รับค่าถัดไปจากคอลเล็กชันในการวนซ้ำแต่ละครั้ง

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

สำหรับแต่ละวง สิ่งที่คอมไพเลอร์เห็น: วนซ้ำด้วยตัววนซ้ำ
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();

while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}

เมื่อคอมไพเลอร์พบfor-eachการวนซ้ำในโค้ดของคุณ ก็จะแทนที่ด้วยโค้ดทางด้านขวา: จะเพิ่มการเรียกใช้เพื่อรับตัววนซ้ำพร้อมกับการเรียกเมธอดอื่นๆ ที่ขาดหายไป

โปรแกรมเมอร์ชอบfor-eachลูปและมักจะใช้มันเมื่อต้องการวนซ้ำองค์ประกอบทั้งหมดของคอลเลกชัน

แม้แต่การวนซ้ำArrayListรายการโดยใช้for-eachการวนซ้ำก็ยังดูสั้นกว่า:

สำหรับแต่ละวง สำหรับลูป
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}


4. การลบองค์ประกอบในfor-eachลูป

ลูfor-eachปมีข้อเสียอย่างหนึ่ง: ไม่สามารถลบองค์ประกอบได้อย่างถูกต้อง หากคุณเขียนโค้ดแบบนี้ คุณจะได้รับข้อผิดพลาด

รหัส บันทึก
ArrayList<String> list = new ArrayList<String>();

list.add("Hello");
list.add("Hello");
list.add("Hola");
list.add("Bonjour");
list.add("Ciao");
list.add("Namaste");

for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}












การดำเนินการลบจะทำให้เกิดข้อผิดพลาด!

นี่เป็นรหัสที่ดีและเข้าใจได้ แต่มันใช้งานไม่ได้

สำคัญ!

คุณไม่สามารถเปลี่ยนคอลเลกชั่นได้ในขณะที่คุณกำลังสำรวจมันด้วยตัววนซ้ำ

มีสามวิธีในการหลีกเลี่ยงข้อจำกัดนี้

1. ใช้ลูปประเภทอื่น

When traversing an ArrayList collectionคุณสามารถใช้ลูปธรรมดากับตัวแปรiตัวนับได้

รหัส
for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);

   if (str.equals("Hello"))
   {
      list.remove(str);
      i--; // We need to decrease i, because the remove operation shifted the elements
   }
}

อย่างไรก็ตามตัวเลือกนี้ไม่เหมาะสำหรับHashSetคอHashMapลเลกชัน

2. ใช้ตัววนซ้ำที่ชัดเจน

คุณสามารถใช้ iterator อย่างชัดเจนและเรียกremove()เมธอด ของมัน

รุ่นที่ใช้งาน เวอร์ชันที่ใช้งานไม่ได้
Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   if (str.equals("Hello"))
       it.remove();
}

for
(String str: list) { if (str.equals("Hello")) list.remove(str); }

โปรดทราบว่าเราเรียกremove()เมธอดบน iterator object! ตัววนซ้ำทราบว่ารายการถูกลบแล้วและสามารถจัดการกับสถานการณ์ได้อย่างถูกต้อง

3. ใช้สำเนาของคอลเลกชัน

คุณยังสามารถสร้างสำเนาของคอลเลกชัน จากนั้นใช้สำเนาในลูfor-eachปและลบองค์ประกอบออกจากคอลเลกชันต้นฉบับ

รหัส บันทึก
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
การสร้างสำเนาของคอลเลกชันนั้นง่ายมาก



การวนซ้ำจะใช้ตัววนซ้ำสำหรับสำเนาของคอลเลกชัน
องค์ประกอบจะถูกลบออกจากlistคอลเลกชัน

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