Programing

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

crosscheck 2020. 5. 15. 20:54
반응형

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


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);

이것은 당신이하고 싶은 조작이 "삭제"라고 가정합니다.

이 방법을 "추가"하려는 경우에도 효과가 있지만 다른 컬렉션을 반복하여 두 번째 컬렉션에 추가 할 요소를 결정한 다음 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방법을 사용하여 정렬 된 세트 또는 여기에 제공된 슬라이싱 방법을 사용하여 유사한 것을 얻을 수 있습니다 .

고려 사항 :

What method you use might depend on what you are intending to do

  • The collect and removeAl technique works with any Collection (Collection, List, Set, etc).
  • The ListIterator technique obviously only works with lists, provided that their given ListIterator implementation offers support for add and remove operations.
  • The Iterator approach would work with any type of collection, but it only supports remove operations.
  • With the ListIterator/Iterator approach the obvious advantage is not having to copy anything since we remove as we iterate. So, this is very efficient.
  • The JDK 8 streams example don't actually removed anything, but looked for the desired elements, and then we replaced the original collection reference with the new one, and let the old one be garbage collected. So, we iterate only once over the collection and that would be efficient.
  • In the collect and removeAll approach the disadvantage is that we have to iterate twice. First we iterate in the foor-loop looking for an object that matches our removal criteria, and once we have found it, we ask to remove it from the original collection, which would imply a second iteration work to look for this item in order to remove it.
  • I think it is worth mentioning that the remove method of the Iterator interface is marked as "optional" in Javadocs, which means that there could be Iterator implementations that throw UnsupportedOperationException if we invoke the remove method. As such, I'd say this approach is less safe than others if we cannot guarantee the iterator support for removal of elements.

Are there any reasons to prefer one approach over the other

The first approach will work, but has the obvious overhead of copying the list.

The second approach will not work because many containers don't permit modification during iteration. This includes ArrayList.

If the only modification is to remove the current element, you can make the second approach work by using itr.remove() (that is, use the iterator's remove() method, not the container's). This would be my preferred method for iterators that support remove().


In Java 8, there is another approach. Collection#removeIf

eg:

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

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

Only second approach will work. You can modify collection during iteration using iterator.remove() only. All other attempts will cause ConcurrentModificationException.


You can't do the second, because even if you use the remove() method on Iterator, you'll get an Exception thrown.

Personally, I would prefer the first for all Collection instances, despite the additional overheard of creating the new Collection, I find it less prone to error during edit by other developers. On some Collection implementations, the Iterator remove() is supported, on other it isn't. You can read more in the docs for Iterator.

The third alternative, is to create a new Collection, iterate over the original, and add all the members of the first Collection to the second Collection that are not up for deletion. Depending on the size of the Collection and the number of deletes, this could significantly save on memory, when compared to the first approach.


I would choose the second as you don't have to do a copy of the memory and the Iterator works faster. So you save memory and time.


There is also a simple solution to "iterate" a Collection and removing each items.

List<String> list = new ArrayList<>();
//Fill the list

It simply conciste on looping until the list is empty, and on each iteration, we remove the first element with remove(0).

while(!list.isEmpty()){
    String s = list.remove(0);
    // do you thing
}

I don't believe this has any improvement compare to the Iterator, it still required to have a mutable list but I like the simplicity of this solution.


why not this?

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

And if it's a map, not a list, you can use keyset()

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

반응형