JavaRush /Java Blog /Random-KO /Java의 ArrayList에서 요소 제거

Java의 ArrayList에서 요소 제거

Random-KO 그룹에 게시되었습니다
안녕하세요! 지난 강의에서 우리는 ArrayList 클래스에 대해 알게 되었고 , 이를 사용하여 가장 일반적인 작업을 수행하는 방법도 배웠습니다. 또한 ArrayList와 일반 배열 사이의 몇 가지 차이점을 강조했습니다. 이제 ArrayList에서 요소를 제거하는 방법을 살펴보겠습니다. 우리는 이미 일반 배열의 요소를 삭제하는 것이 그리 편리하지 않다고 말했습니다. ArrayList에서 요소 제거 - 1셀 자체를 삭제할 수 없으므로 해당 값을 "0"으로 설정할 수만 있습니다.
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