반복하는 동안 컬렉션에서 요소 제거
AFAIK에는 다음 두 가지 접근법이 있습니다.
- 컬렉션의 복사본을 반복하다
- 실제 컬렉션의 반복기 사용
예를 들어.
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에서 삭제 기준과 일치하는 오브젝트를 찾아내고, 그 오브젝트를 발견하면 원래 컬렉션에서 삭제하도록 요청합니다.이것은 이 아이템을 삭제하기 위한 두 번째 반복 작업을 의미합니다. - 이 제거 방법은 언급할 가치가 있다고 생각합니다.
Iterator
Javadocs에서는 인터페이스가 "옵션"으로 표시되어 있습니다.즉, 이 경우,Iterator
도입에 영향을 미치는 영향UnsupportedOperationException
remove 메서드를 호출하면 됩니다.따라서 요소 제거에 대한 반복기 지원을 보장할 수 없다면 이 접근법은 다른 접근법보다 덜 안전하다고 말할 수 있습니다.
Old Timer Favorite(오래된 타이머 즐겨찾기) :
List<String> list;
for(int i = list.size() - 1; i >= 0; --i)
{
if(list.get(i).contains("bad"))
{
list.remove(i);
}
}
이점:
- 목록을 한 번만 반복한다.
- 추가 객체가 생성되지 않거나 기타 불필요한 복잡함
- 제거된 항목의 인덱스를 사용하려고 해도 문제가 없습니다. 왜냐하면...생각해 봐!
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
'programing' 카테고리의 다른 글
vuejs의 v-if와 유사한 사용자 지정 지시문 (0) | 2022.05.27 |
---|---|
Java를 사용하여 기본 요소 배열에서 max/min 값 찾기 (0) | 2022.05.27 |
'readline/readline.h' 파일을 찾을 수 없습니다. (0) | 2022.05.27 |
log4j2에 slf4j를 사용할 가치가 있습니까? (0) | 2022.05.27 |
목록에서 정수의 적절한 삭제 (0) | 2022.05.27 |