programing

반복하는 동안 컬렉션에서 요소 제거

prostudy 2022. 5. 27. 21:19
반응형

반복하는 동안 컬렉션에서 요소 제거

AFAIK에는 다음 두 가지 접근법이 있습니다.

  1. 컬렉션의 복사본을 반복하다
  2. 실제 컬렉션의 반복기 사용

예를 들어.

List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
    // modify actual fooList
}

그리고.

Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
    // modify actual fooList using itr.remove()
}

하나의 접근방식을 다른 접근방식보다 선호하는 이유가 있는가(예를 들어 단순한 가독성 이유로 첫 번째 접근방식을 선호한다)?

이 문제를 피하기 위한 몇 가지 대안과 함께 몇 가지 예를 들어 보겠습니다.ConcurrentModificationException.

우리가 다음과 같은 책을 소장하고 있다고 가정해 보자.

List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));

수집 및 삭제

첫 번째 방법은 삭제할 모든 객체를 수집하는 것입니다(예를 들어 확장 for 루프를 사용). 반복이 완료되면 발견된 모든 객체를 제거합니다.

ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
    if(book.getIsbn().equals(isbn)){
        found.add(book);
    }
}
books.removeAll(found);

이것은, 「삭제」라고 하는 조작을 전제로 하고 있습니다.

이 접근방식을 「추가」하고 싶은 경우는, 다른 컬렉션에 대해서 반복해, 제2의 컬렉션에 추가할 요소를 결정하고 나서, 그 후, 이 어프로치를 발행하는 것으로 합니다.addAll메서드를 사용합니다.

ListIterator 사용

목록으로 작업하는 경우, 다른 기법은 다음과 같이 구성되어 있습니다.ListIterator반복 중 항목 제거 및 추가를 지원합니다.

ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
    if(iter.next().getIsbn().equals(isbn)){
        iter.remove();
    }
}

위의 예에서는 "삭제" 방법을 사용했습니다.이것은 당신의 질문이 암시하는 것처럼 보였지만, 당신은 그 방법을 사용할 수도 있습니다.add메서드를 사용하여 반복 중에 새 요소를 추가합니다.

JDK 사용 > = 8

Java 8 이상의 버전을 사용하는 사용자에게는 이 기능을 활용하기 위해 사용할 수 있는 몇 가지 다른 기술이 있습니다.

새로운 것을 사용할 수 있습니다.removeIf의 메서드Collection기본 클래스:

ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));

또는 새로운 스트림 API를 사용합니다.

ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
                           .filter(b -> b.getIsbn().equals(other))
                           .collect(Collectors.toList());

이 마지막 경우 컬렉션에서 요소를 필터링하려면 필터링된 컬렉션에 원래 참조를 재할당합니다(즉,books = filtered또는 필터링된 컬렉션을 사용하여removeAll원래 컬렉션에서 발견된 요소(즉,books.removeAll(filtered)).

서브리스트 또는 서브셋 사용

다른 대안들도 있다.목록이 정렬되고 연속된 요소를 제거하려면 하위 목록을 만든 다음 지우면 됩니다.

books.subList(0,5).clear();

서브리스트는 원래 리스트에서 백업되므로 이 서브컬렉션 요소를 삭제하는 데 효과적입니다.

정렬된 세트를 사용하여 유사한 작업을 수행할 수 있습니다.NavigableSet.subSet방법 또는 여기에 제공된 슬라이스 방법 중 하나.

고려 사항:

어떤 방법을 사용하느냐에 따라 의도하는 바가 달라질 수 있습니다.

  • 콜렉트removeAl모든 컬렉션(컬렉션, 목록, 세트 등)에서 사용할 수 있습니다.
  • ListIterator기법은 분명히 목록에서만 작동한다, 만약 그들이 주어진다면ListIterator구현은 추가 및 제거 작업을 지원합니다.
  • Iterator접근 방식은 모든 유형의 수집에서 작동하지만 제거 작업만 지원합니다.
  • 를 사용하여ListIterator/Iterator분명한 장점은 반복 삭제하기 때문에 아무것도 복사할 필요가 없다는 것입니다.이것은 매우 효율적입니다.
  • JDK 8 스트림의 예에서는 실제로는 아무것도 삭제하지 않고 원하는 요소를 검색하여 원래 컬렉션 참조를 새 컬렉션으로 교체하고 오래된 컬렉션 참조는 가비지 컬렉션으로 합니다.따라서 컬렉션에 대해 한 번만 반복하면 효율적입니다.
  • 콜렉트 및removeAll단점은 두 번 반복해야 한다는 것입니다.먼저 foor-loop에서 삭제 기준과 일치하는 오브젝트를 찾아내고, 그 오브젝트를 발견하면 원래 컬렉션에서 삭제하도록 요청합니다.이것은 이 아이템을 삭제하기 위한 두 번째 반복 작업을 의미합니다.
  • 이 제거 방법은 언급할 가치가 있다고 생각합니다.IteratorJavadocs에서는 인터페이스가 "옵션"으로 표시되어 있습니다.즉, 이 경우,Iterator도입에 영향을 미치는 영향UnsupportedOperationExceptionremove 메서드를 호출하면 됩니다.따라서 요소 제거에 대한 반복기 지원을 보장할 수 없다면 이 접근법은 다른 접근법보다 덜 안전하다고 말할 수 있습니다.

Old Timer Favorite(오래된 타이머 즐겨찾기) :

List<String> list;

for(int i = list.size() - 1; i >= 0; --i) 
{
        if(list.get(i).contains("bad"))
        {
                list.remove(i);
        }
}

이점:

  1. 목록을 한 번만 반복한다.
  2. 추가 객체가 생성되지 않거나 기타 불필요한 복잡함
  3. 제거된 항목의 인덱스를 사용하려고 해도 문제가 없습니다. 왜냐하면...생각해 봐!

Java 8에서는 다른 접근법이 있습니다.컬렉션 #removeIf

예:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

list.removeIf(i -> i > 2);

다른 접근 방식보다 하나의 접근 방식을 선호하는 이유가 있습니까?

첫 번째 접근 방식은 작동하지만 목록을 복사하는 데 드는 명백한 오버헤드가 있습니다.

많은 컨테이너가 반복 중에 수정을 허용하지 않기 때문에 두 번째 접근 방식은 작동하지 않습니다.여기에는 이 포함됩니다.

변경사항이 현재 요소를 제거하는 것뿐인 경우 를 사용하여 두 번째 접근 방식을 사용할 수 있습니다(반복기 사용).remove()컨테이너가 아닌 방법).이것은 를 지원하는 반복자에게 권장되는 방법입니다.

두 번째 방법만 작동합니다.할 때 할 수 .iterator.remove()도 원인이 .ConcurrentModificationException.

수 두 번째는 할 수 없어요.remove()반복기에서 메서드를 사용하면 예외가 느려집니다.

는 개인적으로 모든 사람에게 첫 .Collection 「」를 하는 것을 instances는 행해지지 않습니다.Collection다른 개발자가 편집하는 동안 오류가 발생할 가능성이 적습니다. Collection(Iterator)가 됩니다.remove()서포트되고 있지 않은 경우도 있습니다.Iterator용 문서에서 자세한 내용을 볼 수 있습니다.

대안은 새로운 입니다.Collection하여 첫 해 주세요.Collection 번째까지Collection삭제 대상이 아닙니다.의 크기에 따라Collection삭제 횟수는 첫 번째 접근법에 비해 메모리를 대폭 절약할 수 있습니다.

메모리 복사를 할 필요가 없고, 반복 작업도 빨라지기 때문에 두 번째를 선택하겠습니다.기억과 시간을 절약할 수 있습니다.

다음 예시를 볼 수 있습니다.목록에서 홀수값을 삭제할 수 있습니다.

public static void main(String[] args) {
    Predicate<Integer> isOdd = v -> v % 2 == 0;
    List<Integer> listArr = Arrays.asList(5, 7, 90, 11, 55, 60);
    listArr = listArr.stream().filter(isOdd).collect(Collectors.toList());
    listArr.forEach(System.out::println);
}

왜 이건 안 돼?

for( int i = 0; i < Foo.size(); i++ )
{
   if( Foo.get(i).equals( some test ) )
   {
      Foo.remove(i);
   }
}

리스트가 아닌 맵이라면 keyset()을 사용할 수 있습니다.

언급URL : https://stackoverflow.com/questions/10431981/remove-elements-from-collection-while-iterating

반응형