Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to "CAST" ArrayList to CopyOnWriteArrayList

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.

like image 559
namalfernandolk Avatar asked Dec 20 '22 04:12

namalfernandolk


2 Answers

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.
like image 70
Petr Janeček Avatar answered Mar 08 '23 07:03

Petr Janeček


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();
like image 45
Sean Patrick Floyd Avatar answered Mar 08 '23 07:03

Sean Patrick Floyd