1. Overview
In this quick article, we’ll be looking at the CopyOnWriteArrayList from the java.util.concurrent package.
This is a very useful construct in the multi-threaded programs – when we want to iterate over a list in a thread-safe way without an explicit synchronization.
2. CopyOnWriteArrayList API
The design of the CopyOnWriteArrayList uses an interesting technique to make it thread-safe without a need for synchronization. When we are using any of the modify methods – such as add() or remove() – the whole content of the CopyOnWriteArrayList is copied into the new internal copy.
Due to this simple fact, we can iterate over the list in a safe way, even when concurrent modification are happening.
When we’re calling the iterator() method on the CopyOnWriteArrayList, we get back an Iterator backed up by the immutable snapshot of the content of the CopyOnWriteArrayList.
Its content is an exact copy of data that is inside an ArrayList from the time when the Iterator was created. Even if in the meantime some other thread adds or removes an element from the list, that modification is making a fresh copy of the data that will be used in any further data lookup from that list.
The characteristics of this data structure make it particularly useful in cases when we are iterating over it more often than we are modifying it. If adding elements is a common operation in our scenario, then CopyOnWriteArrayList won’t be a good choice – because the additional copies will definitely lead to sub-par performance.
3. Iterating Over CopyOnWriteArrayList While Inserting
Let’s say that we’e creating an instance of the CopyOnWriteArrayList that stores integers:
CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8});
Next, we want to iterate over that array, so we are creating an Iterator instance:
Iterator<Integer> iterator = numbers.iterator();
After the Iterator is created, we are adding a new element to the numbers list:
numbers.add(10);
Keep in mind that, when we create an iterator for the CopyOnWriteArrayList, we get an immutable snapshot of the data in the list at the time iterator() was called.
Because of that, while iterating over it, we won’t see the number 10 in the iteration:
List<Integer> result = new LinkedList<>(); iterator.forEachRemaining(result::add); assertThat(result).containsOnly(1, 3, 5, 8);
Subsequent iteration using newly created Iterator will also return the number 10 that was added:
Iterator<Integer> iterator2 = numbers.iterator(); List<Integer> result2 = new LinkedList<>(); iterator2.forEachRemaining(result2::add); assertThat(result2).containsOnly(1, 3, 5, 8, 10);
4. Removing While Iterating is Not Allowed
The CopyOnWriteArrayList was created to allow for the possibility of safe iterating over elements even when the underlying list gets modified.
Because of the copying mechanism, the remove() operation on the returned Iterator is not permitted – resulting with UnsupportedOperationException:
@Test(expected = UnsupportedOperationException.class) public void whenIterateOverItAndTryToRemoveElement_thenShouldThrowException() { CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 8}); Iterator<Integer> iterator = numbers.iterator(); while (iterator.hasNext()) { iterator.remove(); } }
5. Conclusion
In this quick tutorial, we had a look at the CopyOnWriteArrayList implementation from the java.util.concurrent package.
We saw the interesting semantics of this list and how it can be iterated in a thread-safe way, while other threads can continue inserting or removing elements from it.
The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.