remove elements from CopyOnWriteArrayList

2019-02-03 09:35发布

I am getting an exception when I try to remove elements from CopyOnWriteArrayList using an iterator. I have noticed that it is documented

Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.

(from http://download.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html)

Now, surprisingly i can iterate it with foreach and use the remove() function . But then I get the famous bug - when trying to remove an item from a list using a for loop - you skip the element next to the removed element. any suggestions then?

8条回答
Emotional °昔
2楼-- · 2019-02-03 10:13

Iterate over the collection choosing all the elements you want to delete and putting those in a temporary collection. After you finish iteration remove all found elements from the original collection using method removeAll.

Would that work out for you? I mean, not sure if deletion logic is more complicated than that in your algorithm.

查看更多
劳资没心,怎么记你
3楼-- · 2019-02-03 10:15

EDIT: I'm an idiot. I missed the fact that this is a copy-on-write list so every removal means a new copy. So my suggestions below are likely to be suboptimal if there's more than one removal.

Same as for any other list whose iterator doesn't support remove, or anything where you're not using an iterator. There are three basic techniques that come to mind to avoid this bug:

  1. Decrement the index after removing something (being careful not to do anything with the index until the next iteration). For this you'll obviously have to use a for(int i=0; i < ... style of for loop, so that you can manipulate the index.

  2. Somehow repeat what the inside of the loop is doing, without literally going back to the top of the loop. Bit of a hack - I would avoid this technique.

  3. Iterate over the list in reverse (from end to start, instead of from start to end). I prefer this approach as it's the simplest.

查看更多
走好不送
4楼-- · 2019-02-03 10:17

Since this is a CopyOnWriteArrayList it is totally safe to remove elements while iterating with forEach. No need for fancy algorithms.

list.forEach(e -> {
    if (shouldRemove(e))
        list.remove(e);
});

EDIT: Well of course that works if you want to delete elements by reference, not by position.

查看更多
疯言疯语
5楼-- · 2019-02-03 10:25

Something like this:

int pos = 0;
while(pos < lst.size() ) {
  Foo foo = lst.get(pos);
  if( hasToBeRemoved(foo) ) {
    lst.remove(pos);
    // do not move position
  } else {
    pos++;
  }
}
查看更多
Deceive 欺骗
6楼-- · 2019-02-03 10:29

Ususlly you would iterate first gathering elemenet to be deleted in a separate list then delete them outside the for each loop (which is disguised iterator based loop anyway)

查看更多
淡お忘
7楼-- · 2019-02-03 10:29

You could use Queue instead of List.

private Queue<Something> queue = new ConcurrentLinkedQueue<Something>();

It's thread safe and supports iterator.remove(). Be aware of the thread-safe behavior of Queue iterators, though (check the javadoc).

查看更多
登录 后发表回答