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);
이것은 당신이하고 싶은 조작이 “삭제”라고 가정합니다.
이 방법을 “추가”하려는 경우에도 효과가 있지만 다른 컬렉션을 반복하여 두 번째 컬렉션에 추가 할 요소를 결정한 다음 addAll
마지막에 메소드 를 발행한다고 가정합니다 .
ListIterator 사용
리스트로 작업하는 경우 ListIterator
, 반복 기법 중에 항목을 제거하고 추가 할 수 있는 기능을 사용하는 다른 기술이 사용 됩니다.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
다시 한 번, 위 예제에서 “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
접근 방식의 단점은 두 번 반복해야한다는 것입니다. 먼저 제거 루프에서 제거 기준과 일치하는 객체를 찾기 위해 루프에서 반복하고, 일단 찾은 후에는 원래 컬렉션에서 객체를 제거하도록 요청합니다. 이는 다음 항목을 찾기 위해이 항목을 찾기위한 두 번째 반복 작업을 의미합니다. 제거하십시오. Iterator
인터페이스 의 remove 메소드가 Javadocs에서 “선택적”으로 표시 된다는 것을 언급 할 가치가 있다고 생각합니다 . 이는 remove 메소드를 호출하면Iterator
구현 이 발생할 수 있음을 의미합니다UnsupportedOperationException
. 따라서 요소 제거를위한 반복자 지원을 보장 할 수없는 경우이 방법이 다른 방법보다 안전하지 않다고 말하고 싶습니다.
답변
Java 8에는 또 다른 접근법이 있습니다. 컬렉션 #removeIf
예 :
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.removeIf(i -> i > 2);
답변
한 가지 접근법을 다른 접근법보다 선호하는 이유가 있습니까?
첫 번째 방법은 효과가 있지만 목록을 복사하는 명백한 오버 헤드가 있습니다.
많은 컨테이너가 반복 중에 수정을 허용하지 않기 때문에 두 번째 방법은 작동하지 않습니다. 이 포함됩니다ArrayList
.
유일한 수정은 현재 요소를 제거하는 경우, 당신은 사용하여 두 번째 방법 작업을 할 수 있습니다 itr.remove()
(즉, 사용하는 반복자를 의 remove()
방법이 아닌 용기 의). 이것은 지원하는 반복자에 대해 내가 선호하는 방법입니다 remove()
.
답변
두 번째 접근 방식 만 작동합니다. 반복하는 동안 iterator.remove()
만 콜렉션을 수정할 수 있습니다 . 다른 모든 시도는 원인이 ConcurrentModificationException
됩니다.
답변
이전 타이머 즐겨 찾기 (아직 작동) :
List<String> list;
for(int i = list.size() - 1; i >= 0; --i)
{
if(list.get(i).contains("bad"))
{
list.remove(i);
}
}
답변
당신이 사용하는 경우에도 때문에, 두 번째 할 수 없습니다 remove()
에 대한 방법 Iterator를 , 당신이 던져 예외를 얻을 수 있습니다 .
개인적으로, 나는 Collection
새로운 인스턴스 생성에 대한 추가 소식에도 불구하고 모든 인스턴스에 대해 첫 번째를 선호하지만 Collection
다른 개발자가 편집하는 동안 오류가 발생하기 쉽습니다. 일부 Collection 구현에서는 Iterator remove()
가 지원되지만 그렇지 않은 경우에는 Iterator 가 지원됩니다. Iterator 관련 문서에서 더 많은 내용을 읽을 수 있습니다 .
세 번째 대안은 새를 만드는 것입니다 Collection
원래 이상 반복, 그리고 첫 번째의 모든 구성원을 추가 Collection
두 번째에 Collection
있습니다 없습니다 까지 삭제. Collection
삭제 크기 및 삭제 수에 따라 첫 번째 방법과 비교할 때 메모리를 크게 절약 할 수 있습니다.
답변
메모리 사본을 할 필요가 없으므로 Iterator가 더 빨리 작동하므로 두 번째를 선택합니다. 따라서 메모리와 시간을 절약 할 수 있습니다.