4

I know that, casting is not the correct word when

java.lang.Object
  -> java.util.concurrent.CopyOnWriteArrayList<E>

and

java.lang.Object 
  -> java.util.AbstractCollection<E>
      -> java.util.AbstractList<E>
          -> java.util.ArrayList<E>

But what I want is adding the behavior of the CopyOnWriteArrayList to the ArrayList.

Eg :
I want to do following. But tstArry is an ArrayList. not a CopyOnWriteArrayList

for(TestCls testCls : tstArry)
    if(testCls.getVal1().equals("a1"))
        tstArry.remove(testCls);

Or is this the only way to get the job done?

for(int i = 0; i < tstArry.size(); i++)
    if(tstArry.get(i).getVal1().equals("a1"))
        tstArry.remove(i--);

tstArry is an ArrayList from a class that I haven't had the control on it. So, Please make the changing the type of ArrayList to another is the far most solution.

ironwood
  • 8,936
  • 15
  • 65
  • 114
  • If I understand your question correctly, you want to remove an element while iterating. For that, use the iterator, [see this question](http://stackoverflow.com/questions/3184883/concurrentmodificationexception-for-arraylist) – Vincent van der Weele Jun 21 '13 at 08:46
  • "what I want is adding the behavior of the CopyOnWriteArrayList to the ArrayList" <-- why not use a `CopyOnWriteArrayList` from the start then? – fge Jun 21 '13 at 08:47
  • 2
    And also note that both are `List`s, ultimately; if you want to initiate one, you should do `List = new SomeListImplementation<>();`, not use `SomeListImplementation = ...` – fge Jun 21 '13 at 08:48

3 Answers3

6

You can't remove() from iterated collection when using the enhanced for-each loop. The for-each loop uses Iterator<TestCls> implicitly. The JavaDoc clearly states that

The iterators returned by this class's iterator() and listIterator() methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove() or add() methods, the iterator will throw a ConcurrentModificationException.

The for-each loop creates an iterator internally and uses it to traverse the list. Then you change the structure of the list ... and the iterator has to fail. The thing is that you don't have access to the iterators methods, so you have to use Iterator<TestCls> explicitly. The generated traversing bytecode will be the same, the only difference being you being able to remove elements from the list as traverse it.

for (Iterator<TestCls> iter = tstArry.iterator(); iter.hasNext(); ) {
    TextCls testCls = iter.next();
    if(testCls.getVal1().equals("a1")) {
        iter.remove();
    }
}

Clarifying EDIT as you are obviously not familiar with iterators and their function. From the Oracle tutorial on Collections:

An Iterator is an object that enables you to traverse through a collection and to remove elements from the collection selectively, if desired. You get an Iterator for a collection by calling its iterator() method.

Note that Iterator.remove() is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.

Use Iterator instead of the for-each construct when you need to:

  • Remove the current element. The for-each construct hides the iterator, so you cannot call remove(). Therefore, the for-each construct is not usable for filtering.
Petr Janeček
  • 37,768
  • 12
  • 121
  • 145
  • Thanx Slanec. But tstArry is an ArrayList from a class that I haven't had the control on it. I tried for a solution without changing the type of the tstArry. – ironwood Jun 21 '13 at 08:57
  • 1
    @oneliner This solution does _not_ change the type of your Collection! I assumed it will be an `ArrayList`, but it can be any other `Iterable` and it won't matter, no changes needed! The only change you have to make is in your for loop. Try copypasting my solution and you'll see it compiles and works :). – Petr Janeček Jun 21 '13 at 09:02
  • 2
    I think the confusion here is that your can call list.remove() inside a loop with a CopyOnWriteArrayList because it uses a snapshot iterator. However using an iterator (as said by Slanec) is the recommended method – Guillaume Jun 21 '13 at 09:36
  • Oh. I missed that Collection extends Iterable. Thanx Slanec. +1 for the kind explanation. – ironwood Jun 21 '13 at 09:53
4

The key is Effective Java, Item 39: Make defensive copies when needed

Take the list you get from the other class. Make a copy of it:

List<TestCls> myCopy = new ArrayList<>(theOtherList);

Then remove the unwanted elements from your copy, not the original collection. To do this, you'll need to use the Iterator.remove() method.

Or you use a library like Guava that lets you filter a collection using predicates:

Predicate<TestCls> predicate = new Predicate<TestCls>(){
   public boolean apply(TestCls data){
       // add check here
   }
};
List<TestCls> myList = 
    FluentIterable.from(theOtherList).filter(predicate).toList();
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
2

You can use the iterator’s remove method to achieve this:

Iterator<TestCls> i = tstArry.iterator();

while (i.hasNext()) {
    TestCls value = i.next();
    if (value.getVal1().equals("a1"))
        i.remove();
}

However, it should be noted that this is largely orthogonal to the CopyOnWriteArrayList which has a distinct use-case.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214