안녕! 지난 수업에서 우리는 클래스에 대해 알게 되었고 ArrayList이 클래스로 가장 일반적인 작업을 수행하는 방법을 배웠습니다. 또한 an ArrayList과 일반 배열의 몇 가지 차이점을 지적했습니다. 그러나 우리는 한 가지 주제 , ArrayList . 지금 논의하겠습니다. ArrayList에서 요소 삭제 - 1일반 배열에서 요소를 삭제하는 것이 그다지 편리하지 않다고 이미 언급했습니다. 요소 자체를 삭제할 수 없기 때문에 값을 "제로아웃"(null로 설정)할 수만 있습니다.

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이를 위해 Rishi의 수업에서 소개한 빠른 루프(for-each 루프라고도 함)를 사용합니다 .

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);
}
코드는 완벽하게 논리적으로 보입니다. 그러나 그 결과는 매우 놀랍습니다. java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)의 java.util.ArrayList$Itr.next(ArrayList. java:831) at Cat.main(Cat.java:25) 일종의 오류가 있으며 발생한 이유가 명확하지 않습니다. 이 프로세스에는 해결해야 하는 여러 가지 뉘앙스가 포함됩니다. 기억해야 할 일반적인 규칙은 다음과 같습니다. 컬렉션을 반복하면서 동시에 해당 요소를 변경할 수 없습니다. 그리고 우리는 단순히 제거가 아니라 모든 종류의 변화를 의미합니다. 고양이 제거를 새 고양이를 삽입하려는 시도로 바꾸면 결과는 동일합니다.

for (Cat cat: cats) {

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

System.out.println(cats);
"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클래스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 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>). 핵심은 반복자가 원래 작업을 쉽게 처리한다는 것입니다. 예를 들어 "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. 글쎄, 거의 모든 것. 다음 강의에서는 이 클래스 내부를 살펴보고 다양한 메서드 호출 중에 어떤 일이 발생하는지 살펴보겠습니다 :) 그때까지!