October 11, 2022

Java CopyOnWriteArrayList With Examples

CopyOnWriteArrayList in Java implements the List interface just like its other well known counterpart ArrayList and is part of java.util.concurrent package. How CopyOnWriteArrayList differs from ArrayList is that it is a thread-safe variant of ArrayList.

For more differences between CopyOnWriteArrayList and ArrayList in Java refer this post- Difference Between ArrayList And CopyOnWriteArrayList in Java

How is Java CopyOnWriteArrayList thread-safe

CopyOnWriteArrayList in Java, just like ArrayList, uses an array of type Object to store its elements. For thread safety, implementation of CopyOnWriteArrayList, as its name suggests, creates a new copy of the underlying array for any modification operation like add, set, replace etc.

That makes CopyOnWriteArrayList a good choice when there are more traversal operations than the mutations, as the List can be iterated along with concurrent modifications with out any inference as the iteration will be done on a separate copy of the List.

Java CopyOnWriteArrayList constructors

  • CopyOnWriteArrayList()- Creates an empty list.
  • CopyOnWriteArrayList(Collection<? extends E> c)- Creates a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.
  • CopyOnWriteArrayList(E[] toCopyIn)- Creates a list holding a copy of the given array.

Java example creating a CopyOnWriteArrayList

Here is a simple example showing how to create CopyOnWriteArrayList and add elements to it.

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyList {
  public static void main(String[] args) {
    //creating CopyOnWriteArrayList
    List<String> carList = new CopyOnWriteArrayList<String>();
    carList.add("Audi");
    carList.add("Jaguar");
    carList.add("Mini Cooper");
    carList.add("BMW");
    System.out.println("List elements- " + carList);
  }
}
Output
List elements- [Audi, Jaguar, Mini Cooper, BMW]

CopyOnWriteArrayList returns a fail-safe iterator

Iterator returned by CopyOnWriteArrayList in Java is fail-safe which means iterator is guaranteed not to throw ConcurrentModificationException even if the List is structurally modified at any time after the iterator is created.

When an iterator is created for the CopyOnWriteArrayList it gets an immutable copy of the underlying array which is iterated. This array never changes during the lifetime of the iterator, so interference is impossible.

But note that since iteration is done on a separate copy so any modification in the CopyOnWriteArrayList won’t be reflected while iteration.

CopyOnWriteArrayList iteration example

Let’s see an example of iteration in CopyOnWriteArrayList. To make it clearer first we’ll iterate an ArrayList while it is also modified concurrently by another thread to see what happens in the case of ArrayList then we’ll see the same example using CopyOnWriteArrayList.

public class CopyList {
  public static void main(String[] args) {
    //creating CopyOnWriteArrayList
    List<String> carList = new ArrayList<String>();
    carList.add("Audi");
    carList.add("Jaguar");
    carList.add("Mini Cooper");
    carList.add("BMW");
    Thread t1 = new Thread(new ItrClass(carList));
    Thread t2 = new Thread(new ModClass(carList));
    t1.start();
    t2.start();
    try {
      t1.join();
      t2.join();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    System.out.println("List elements in Main- " + carList);
  }
}

// Thread class for iteration
class ItrClass implements Runnable{
  List<String> carList; 
  public ItrClass(List<String> carList){
    this.carList = carList;
  }
  @Override
  public void run() {
    Iterator<String> i = carList.iterator(); 
    while (i.hasNext()){ 
      System.out.println(i.next()); 
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } 
    }     
  }
}

//Thread class for modifying list
class ModClass implements Runnable{
  List<String> carList; 
  public ModClass(List<String> carList){
    this.carList = carList;
  }
  @Override
  public void run() {
    System.out.println("Adding new value to the list"); 
    carList.add("Mercedes");  
  }     
}
Output
Adding new value to the list
Audi
Exception in thread "Thread-0" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:937)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:891)
	at com.knpcode.ItrClass.run(CopyList.java:41)
	at java.base/java.lang.Thread.run(Thread.java:844)
List elements in Main- [Audi, Jaguar, Mini Cooper, BMW, Mercedes]

As you can see with ArrayList if list is modified while it is being iterated then the ConcurrentModificationException is thrown.

Using CopyOnWriteArrayList

Now we can change the ArrayList to CopyOnWriteArrayList in the same example.

List<String> carList = new CopyOnWriteArrayList<String>();

With that output is

Adding new value to the list
Audi
Jaguar
Mini Cooper
BMW
List elements in Main- [Audi, Jaguar, Mini Cooper, BMW, Mercedes]

As you can see now ConcurrentModificationException is not thrown but the iterator doesn’t display the newly added element as it is iterating on a separate copy.

Iterator’s add, remove method not permitted in CopyOnWriteArrayList

Making a new copy gives you the convenience to iterate the List without the fear of ConcurrentModificationException but at the same time iterator’s element-changing operations like remove, set, and add are not supported in CopyOnWriteArrayList. These methods throw UnsupportedOperationException.

public class CopyList {
  public static void main(String[] args) {
    //creating CopyOnWriteArrayList
    List<String> carList = new CopyOnWriteArrayList<String>();
    carList.add("Audi");
    carList.add("Jaguar");
    carList.add("Mini Cooper");
    carList.add("BMW");
    Iterator<String> itr = carList.iterator(); 
    while (itr.hasNext()){ 
      String str = itr.next();
      if(str.equals("Jaguar")) {
        // removing using iterator's remove method
        itr.remove();
      }
    }
  }
}
Output
Exception in thread "main" java.lang.UnsupportedOperationException 
at java.base/java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1117)
at com.knpcode.CopyList.main(CopyList.java:21)

As you can see using iterator’s remove method here results in UnsupportedOperationException being thrown.

Advantages and disadvantages of using CopyOnWriteArrayList in Java

CopyOnWriteArrayList performs well when there are more traversal operations than the mutations, since you don't need to explicitly synchronize the CopyOnWriteArrayList to iterate it in a multi-threaded environment.

In general using CopyOnWriteArrayList is costly because of the added task of creating copies in case of mutative operations and keep changing the underlying array.

CopyOnWriteArrayList is guaranteed to not throw ConcurrentModificationException even if there are concurrent modifications to the list while iteration. At the same time iterator’s element-changing operations are not supported.

That's all for the topic Java CopyOnWriteArrayList With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment