JavaRush /จาวาบล็อก /Random-TH /การลบองค์ประกอบออกจาก ArrayList ใน Java

การลบองค์ประกอบออกจาก ArrayList ใน Java

เผยแพร่ในกลุ่ม
สวัสดี! ในการบรรยายครั้งล่าสุด เราได้ทำความคุ้นเคยกับ คลาส ArrayListและยังได้เรียนรู้วิธีการดำเนินการทั่วไปกับคลาสนี้ด้วย นอกจากนี้ เราได้ระบุความแตกต่างเล็กน้อยระหว่าง ArrayList และอาร์เรย์ปกติ ตอนนี้เรามาดูการลบองค์ประกอบออกจาก ArrayList เราได้กล่าวไปแล้วว่าการลบองค์ประกอบในอาเรย์ปกตินั้นไม่สะดวกนัก การลบองค์ประกอบออกจาก ArrayList - 1เนื่องจากเราไม่สามารถลบเซลล์ได้เอง เราจึงทำได้แค่ "ศูนย์" ค่าของมันเท่านั้น:
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("Hippopotamus");
       cats[2] = new Cat("Philip Markovich");

       cats[1] = null;

       System.out.println(Arrays.toString(cats));
   }


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
บทสรุป:

[Cat{name='Томас'}, null, Cat{name='Фorпп Маркович'}]
แต่เมื่อรีเซ็ต "รู" จะยังคงอยู่ในอาเรย์ เราไม่ได้ลบเซลล์ แต่ลบเฉพาะเนื้อหาเท่านั้น ลองนึกภาพว่าจะเกิดอะไรขึ้นถ้าเรามีแมว 50 ตัว โดย 17 ตัวที่เราลบด้วยวิธีนี้ เราจะมีอาร์เรย์ที่มี 17 หลุม และดูแลพวกมัน! การจดจำด้วยใจว่าจำนวนเซลล์ว่างที่คุณสามารถเขียนค่าใหม่นั้นไม่สมจริง ทำผิดพลาดเพียงครั้งเดียวและคุณจะเขียนทับเซลล์ด้วยการอ้างอิงวัตถุที่ต้องการ แน่นอนว่ามีโอกาสที่จะทำอย่างระมัดระวังมากขึ้นอีกเล็กน้อย: หลังจากลบแล้ว ให้ย้ายองค์ประกอบของอาเรย์ไปที่จุดเริ่มต้นเพื่อให้ "รู" อยู่ที่ส่วนท้าย:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Hippopotamus");
   cats[2] = new Cat("Philip Markovich");
   cats[3] = new Cat("Fluff");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       //move the elements to the beginning so that the empty cell is at the end
       cats[i-1] = cats[i];
       cats[i] = null;
   }

   System.out.println(Arrays.toString(cats));
}
บทสรุป:

[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}, null]
ตอนนี้ดูเหมือนว่าจะดูดีขึ้น แต่แทบจะเรียกได้ว่าเป็นวิธีแก้ปัญหาที่เสถียรไม่ได้ อย่างน้อยที่สุดเพราะเราจะต้องเขียนโค้ดนี้ด้วยมือทุกครั้งที่เราลบองค์ประกอบออกจากอาร์เรย์! ตัวเลือกที่ไม่ดี คุณสามารถไปทางอื่นและสร้างวิธีแยกต่างหาก:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift 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("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
เราส่งดัชนีของอ็อบเจ็กต์ของเราไปยังเมธอด และมันถูกลบออก (เหมือนกับในอาร์เรย์) วิธีการนี้remove()มีคุณสมบัติสองประการ ประการแรกมันไม่ทิ้ง "หลุม" มันใช้ตรรกะของการเปลี่ยนองค์ประกอบเมื่อลบองค์ประกอบออกจากตรงกลาง ซึ่งก่อนหน้านี้เราเขียนด้วยมือ ดูผลลัพธ์ของโค้ดก่อนหน้าในคอนโซล:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
เราเอาแมวตัวหนึ่งออกจากตรงกลาง และตัวอื่นๆ ก็ถูกย้ายไปรอบๆ เพื่อไม่ให้มีช่องว่าง ประการที่สองมันสามารถลบวัตถุได้ไม่เพียงแต่โดยดัชนี (เช่นอาร์เรย์ปกติ) แต่ยังโดยการอ้างอิงถึงวัตถุ ด้วย :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(philipp);

   System.out.println(cats.toString());
}
บทสรุป:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
วิธีนี้จะสะดวกมากหากคุณไม่ต้องการเก็บดัชนีของวัตถุที่ต้องการไว้ในหัวเสมอไป ดูเหมือนว่าเราจะจัดการกับการลบตามปกติแล้ว ทีนี้ลองจินตนาการถึงสถานการณ์นี้: เราต้องการวนซ้ำรายการองค์ประกอบของเราและลบแมวที่มีชื่อเฉพาะ ออก สำหรับสิ่งนี้เราใช้ตัวดำเนินการวนซ้ำพิเศษfor- for each. คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในการ บรรยายนี้
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);

   for (Cat cat: cats) {

       if (cat.name.equals("Hippopotamus")) {
           cats.remove(cat);
       }
   }

   System.out.println(cats);
}
ดูเหมือนว่าโค้ดจะดูค่อนข้างสมเหตุสมผล อย่างไรก็ตาม ผลลัพธ์อาจทำให้คุณประหลาดใจ:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
มีข้อผิดพลาดบางอย่างและไม่ชัดเจนว่าเหตุใดจึงปรากฏขึ้นอย่างกะทันหัน มีความแตกต่างหลายประการในกระบวนการนี้ที่ต้องจัดการ กฎทั่วไปที่คุณต้องจำ: คุณไม่สามารถวนซ้ำคอลเลกชันและเปลี่ยนแปลงองค์ประกอบในเวลาเดียวกันได้ ใช่ ใช่ เป็นการเปลี่ยนแปลงจริงๆ และไม่ใช่แค่การลบออก หากคุณลองใช้โค้ดของเราเพื่อแทนที่การลบแมวด้วยการแทรกอันใหม่ ผลลัพธ์จะเหมือนเดิม:
for (Cat cat: cats) {

   cats.add(new Cat("Salem Saberhegen"));
}

System.out.println(cats);

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
เราเปลี่ยนการดำเนินการหนึ่งไปเป็นอีกการดำเนินการหนึ่ง แต่ผลลัพธ์ไม่เปลี่ยนแปลง: ข้อผิดพลาดConcurrentModificationExceptionเดียวกัน มันเกิดขึ้นอย่างแน่นอนเมื่อเราพยายามฝ่าฝืนกฎและเปลี่ยนแปลงรายการในขณะที่วนซ้ำกฎนั้น ใน Java เพื่อลบองค์ประกอบระหว่างการวนซ้ำ คุณจำเป็นต้องใช้วัตถุพิเศษ - ตัววนซ้ำ (คลาสIterator) ชั้นเรียนIteratorมีหน้าที่รับผิดชอบในการเดินผ่านรายการองค์ประกอบต่างๆ อย่างปลอดภัย มันค่อนข้างง่ายเพราะมีเพียง 3 วิธี:
  • hasNext()- ส่งคืนtrueทั้งนี้falseขึ้นอยู่กับว่ามีองค์ประกอบถัดไปในรายการ หรือว่าเรามาถึงองค์ประกอบสุดท้ายแล้วหรือไม่
  • 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 next element
   System.out.println(nextCat);// print it to the console
}
บทสรุป:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='Фorпп Маркович'}
Cat{name='Пушок'}
อย่างที่คุณเห็น คลาสArrayListได้ใช้วิธีการพิเศษในการสร้างตัววนซ้ำiterator()- นอกจากนี้ โปรดทราบว่าเมื่อสร้างตัววนซ้ำ เราจะระบุคลาสของอ็อบเจ็กต์ที่จะใช้งาน ( <Cat>) ท้ายที่สุดแล้ว เราสามารถแก้ไขปัญหาเดิมของเราได้อย่างง่ายดายโดยใช้ตัววนซ้ำ ตัวอย่างเช่น ลองลบแมวชื่อ "Philip Markovich":
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 next element
   if (nextCat.name.equals("Philip Markovich")) {
       catIterator.remove();//delete the cat with the desired name
   }
}

System.out.println(cats);
บทสรุป:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
คุณอาจสังเกตเห็นว่าเราไม่ได้ระบุดัชนีองค์ประกอบหรือชื่อตัวแปรอ้างอิงในเมธอดตัววนซ้ำremove()! ตัววนซ้ำฉลาดกว่าที่คิด: วิธีremove()การลบองค์ประกอบสุดท้ายที่ส่งคืนโดยตัววนซ้ำ อย่างที่คุณเห็น มันทำงานได้ตรงตามที่ต้องการ :) นั่นคือทุกสิ่งที่คุณจำเป็นต้องรู้เกี่ยวกับการลบองค์ประกอบออกจากArrayList. แม่นยำยิ่งขึ้น - เกือบทุกอย่าง ในการบรรยายครั้งต่อไป เราจะมาดู "สิ่งที่อยู่ภายใน" ของชั้นเรียนนี้ และดูว่าเกิดอะไรขึ้นที่นั่นระหว่างปฏิบัติการ :) แล้วเจอกัน!
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION