สวัสดี! ในบทเรียนที่แล้ว เราได้ทำความคุ้นเคยกับ
เราได้กล่าวไปแล้วว่าการลบองค์ประกอบออกจากอาร์เรย์ธรรมดานั้นไม่สะดวกนัก เนื่องจากเราไม่สามารถลบองค์ประกอบได้ เราจึงทำได้เพียง "ศูนย์ออก" (ตั้งค่าเป็น null) ค่าของมัน:
ArrayList
ชั้นเรียนและเรียนรู้วิธีดำเนินการทั่วไปกับชั้นเรียนนี้ นอกจากนี้ เรายังชี้ให้เห็นความแตกต่างหลายประการระหว่างArrayList
อาร์เรย์ธรรมดาและอาร์เรย์ แต่เรามองข้ามหัวข้อหนึ่งไป นั่นคือ วิธีลบองค์ประกอบออกจากไฟล์ArrayList
. เราจะหารือกันตอนนี้ 
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public static void main(String[] args) {
Cat[] cats = new Cat[3];
cats[0] = new Cat("Thomas");
cats[1] = new Cat("Behemoth");
cats[2] = new Cat("Lionel Messi");
cats[1] = null;
System.out.println(Arrays.toString(cats));
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
ผลลัพธ์: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] แต่การตั้งค่าองค์ประกอบอาร์เรย์เป็น null ทำให้ "รู" เรายังไม่ได้ลบตำแหน่งในอาร์เรย์ เฉพาะเนื้อหาเท่านั้น ลองนึกภาพว่าจะเกิดอะไรขึ้นถ้าเรามีแมว 50 ตัวและกำจัดพวกมัน 17 ตัวด้วยวิธีนี้ เราจะมีอาร์เรย์ที่มี 17 รู เพียงแค่พยายามติดตามพวกเขา! มันไม่สมจริงที่จะคาดหวังให้จำจำนวนเซลล์ว่างที่คุณสามารถเขียนค่าใหม่ได้ หากคุณทำผิดพลาด คุณจะเขียนทับการอ้างอิงวัตถุที่คุณต้องการ แน่นอนว่ามีวิธีทำอย่างระมัดระวังกว่านี้เล็กน้อย: หลังจากลบองค์ประกอบแล้วให้ย้ายองค์ประกอบไปที่ด้านหน้าของอาร์เรย์เพื่อใส่ "รู" ที่ส่วนท้าย:
public static void main(String[] args) {
Cat[] cats = new Cat[4];
cats[0] = new Cat("Thomas");
cats[1] = new Cat("Behemoth");
cats[2] = new Cat("Lionel Messi");
cats[2] = new Cat("Fluffy");
cats[1] = null;
for (int i = 2; i < cats.length-1; i++) {
cats [i-1] = cats [i];// Move the elements to the front of the array, so the empty position is at the end
}
System.out.println(Arrays.toString(cats));
}
เอาต์พุต: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] ดูเหมือนจะดีกว่า แต่แทบจะเรียกได้ว่าเป็นโซลูชันที่มีประสิทธิภาพ หากไม่มีเหตุผลอื่นนอกจากความจริงที่ว่าเราต้องเขียนโค้ดนี้ทุกครั้งที่เราลบองค์ประกอบออกจากอาร์เรย์! นี่เป็นตัวเลือกที่ไม่ดี เราสามารถไปอีกทางหนึ่งและสร้างวิธีการแยกต่างหากได้:
public void deleteCat(Cat[] cats, int indexToDelete) {
//...delete the cat corresponding to the index and move the elements
}
แต่ก็มีประโยชน์เพียงเล็กน้อย: วิธีนี้ใช้ได้กับ วัตถุ เท่านั้นCat
แต่ไม่ใช่ประเภทอื่น กล่าวอีกนัยหนึ่ง ถ้าโปรแกรมมีคลาสอีก 100 คลาสที่เราต้องการใช้กับอาร์เรย์ เราจะต้องเขียนเมธอดเดียวกันโดยใช้ตรรกะเดียวกันทุกประการในแต่ละคลาส นี่มันหายนะชัดๆ -_- แต่ArrayList
ชั้นแก้ปัญหานี้ได้! มันใช้วิธีการพิเศษในการลบองค์ประกอบ:remove()
public static void main(String[] args) {
ArrayList<Cat> cats = new ArrayList<>();
Cat thomas = new Cat("Thomas");
Cat behemoth = new Cat("Behemoth");
Cat lionel = new Cat("Lionel Messi");
Cat fluffy = new Cat ("Fluffy");
cats.add(thomas);
cats.add(behemoth);
cats.add(lionel);
cats.add(fluffy);
System.out.println(cats.toString());
cats.remove(1);
System.out.println(cats.toString());
}
เราส่งดัชนีวัตถุของเราไปยังเมธอด ซึ่งจะลบออก (เหมือนกับในอาร์เรย์) วิธีการ นี้remove()
มีคุณสมบัติพิเศษสองประการ ประการแรกมันไม่ทิ้ง "รู" มันใช้ตรรกะที่จำเป็นในการเปลี่ยนองค์ประกอบอยู่แล้วเมื่อองค์ประกอบถูกลบออกจากตรงกลาง ซึ่งก่อนหน้านี้เราเขียนเอง ดูผลลัพธ์จากรหัสก่อนหน้า:
[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
เราย้ายแมวหนึ่งตัวออกจากตรงกลาง และย้ายที่เหลือเพื่อไม่ให้มีที่ว่าง ประการที่สองมันสามารถลบออบเจกต์ได้ไม่เพียงแค่ตามดัชนี (เช่น อาร์เรย์ปกติ) แต่ยังสามารถลบออบเจกต์ได้ ด้วย :
public static void main(String[] args) {
ArrayList<Cat> cats = new ArrayList<>();
Cat thomas = new Cat("Thomas");
Cat behemoth = new Cat("Behemoth");
Cat lionel = new Cat("Lionel Messi");
Cat fluffy = new Cat ("Fluffy");
cats.add(thomas);
cats.add(behemoth);
cats.add(lionel);
cats.add(fluffy);
System.out.println(cats.toString());
cats.remove(lionel);
System.out.println(cats.toString());
}
ผลลัพธ์: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}] [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] วิธีนี้สะดวกมากหากคุณไม่ต้องการติดตามดัชนีของวัตถุที่ต้องการตลอดเวลา ดูเหมือนว่าเราจะพบการลบตามปกติ ลองจินตนาการถึงสถานการณ์นี้: เราต้องการทำซ้ำรายการของเราและลบแมวที่มีชื่อเฉพาะ ในการทำเช่นนี้ เราจะใช้for
การวนรอบแบบเร็ว (หรือที่เรียกว่า for-each loop) ซึ่งเราได้รับการแนะนำในบทเรียนของ Rishi:
public static void main(String[] args) {
ArrayList<Cat> cats = new ArrayList<>();
Cat thomas = new Cat("Thomas");
Cat behemoth = new Cat("Behemoth");
Cat lionel = new Cat("Lionel Messi");
Cat fluffy = new Cat ("Fluffy");
cats.add(thomas);
cats.add(behemoth);
cats.add(lionel);
cats.add(fluffy);
for (Cat cat: cats) {
if (cat.name.equals("Behemoth")) {
cats.remove(cat);
}
}
System.out.println(cats);
}
รหัสดูสมเหตุสมผลอย่างสมบูรณ์ แต่ผลลัพธ์อาจสร้างความประหลาดใจอย่างมาก: ข้อยกเว้นในเธรด "main" java.util.ConcurrentModificationException ที่ java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) ที่ java.util.ArrayList$Itr.next(ArrayList. java:831) ที่ Cat.main(Cat.java:25) มีข้อผิดพลาดบางอย่าง และไม่ชัดเจนว่าเหตุใดจึงเกิดขึ้น กระบวนการนี้เกี่ยวข้องกับความแตกต่างหลายประการที่ต้องได้รับการแก้ไข นี่คือกฎทั่วไปที่คุณต้องจำไว้: คุณไม่สามารถวนซ้ำคอลเลกชันและเปลี่ยนองค์ประกอบพร้อมกันได้ และเราหมายถึงการเปลี่ยนแปลงทุกประเภท ไม่ใช่แค่การกำจัดเท่านั้น หากคุณแทนที่การนำแมวออกด้วยการพยายามใส่แมวใหม่ ผลลัพธ์จะเหมือนเดิม:
for (Cat cat: cats) {
cats.add(new Cat("Salem Saberhagen"));
}
System.out.println(cats);
ข้อยกเว้นในเธรด "main" java.util.ConcurrentModificationException ที่ java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) ที่ java.util.ArrayList$Itr.next(ArrayList.java:831) ที่ Cat.main( Cat.java:25) เราเปลี่ยนการดำเนินการหนึ่งเป็นอีกการดำเนินการหนึ่ง แต่ผลลัพธ์ไม่เปลี่ยนแปลง: เราได้รับ ConcurrentModificationExceptionเดียวกัน มันเกิดขึ้นเมื่อเราพยายามทำลายกฎด้านบนโดยเปลี่ยนรายการในขณะที่วนซ้ำ ใน Java เราต้องการวัตถุพิเศษที่เรียกว่าตัววนซ้ำ (Iterator
คลาส) เพื่อลบรายการในขณะที่วนซ้ำคอลเลกชัน ชั้นIterator
เรียนมีหน้าที่รับผิดชอบในการวนซ้ำรายการองค์ประกอบอย่างปลอดภัย มันค่อนข้างง่ายเนื่องจากมีเพียง 3 วิธี:
hasNext()
- คืนค่าจริงหรือเท็จ ขึ้นอยู่กับว่ามีรายการถัดไปในรายการ หรือเราไปถึงรายการสุดท้ายแล้วnext()
- ส่งคืนรายการถัดไปในรายการremove()
- ลบรายการออกจากรายการ
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list
Cat nextCat = catIterator.next();// Get the next element
System.out.println(nextCat);// Display it
}
ผลลัพธ์: Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} อย่างที่คุณเห็น พวกมันได้ใช้วิธีพิเศษใน การArrayList
สร้าง ตัวทำซ้ำ: iterator()
. นอกจากนี้ โปรดทราบว่าเมื่อเราสร้างตัววนซ้ำ เราจะระบุคลาสของออบเจกต์ที่จะใช้งานด้วย ( <Cat>
) บรรทัดล่างคือ iterator จัดการงานดั้งเดิมของเราได้อย่างง่ายดาย ตัวอย่างเช่น ลบแมวชื่อ "Lionel Messi":
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list
Cat nextCat = catIterator.next();// Get the next element
if (nextCat.name.equals("Lionel Messi")) {
catIterator.remove();// Delete the cat with the specified name
}
}
System.out.println(cats);
ผลลัพธ์: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] คุณอาจสังเกตว่าเราไม่ได้ระบุดัชนีหรือชื่อในเมธอดของตัววนremove()
ซ้ำ ! ตัววนซ้ำนั้นฉลาดกว่าที่ปรากฏ: remove()
ลบองค์ประกอบสุดท้ายที่ส่งคืนโดยตัววนซ้ำ อย่างที่คุณเห็น มันทำในสิ่งที่เราต้องการ :) โดยหลักการแล้ว นี่คือทุกสิ่งที่คุณจำเป็นต้องรู้เกี่ยวกับการลบองค์ประกอบออกจากไฟล์ArrayList
. เกือบทุกอย่าง ในบทเรียนต่อไป เราจะดูในคลาสนี้และดูว่าเกิดอะไรขึ้นที่นั่นระหว่างการเรียกเมธอดต่างๆ :) จนกว่าจะถึงตอนนั้น!
GO TO FULL VERSION