Avoiding the ConcurrentModificationException in Java – 避免Java中的ConcurrentModificationException

最后修改: 2017年 2月 8日

1. Introduction


In this article, we’ll take a look at the ConcurrentModificationException class.


First, we’ll give an explanation how it works, and then prove it by using a test for triggering it.


Finally, we’ll try out some workarounds by using practical examples.


2. Triggering a ConcurrentModificationException


Essentially, the ConcurrentModificationException is used to fail-fast when something we are iterating on is modified. Let’s prove this with a simple test:


@Test(expected = ConcurrentModificationException.class)
public void whilstRemovingDuringIteration_shouldThrowException() throws InterruptedException {

    List<Integer> integers = newArrayList(1, 2, 3);

    for (Integer integer : integers) {

As we can see, before finishing our iteration we are removing an element. That’s what triggers the exception.


3. Solutions


Sometimes, we might actually want to remove elements from a collection whilst iterating. If this is the case, then there are some solutions.


3.1. Using an Iterator Directly


A for-each loop uses an Iterator behind the scenes but is less verbose. However, if we refactored our previous test to use an Iterator, we will have access to additional methods, such as remove(). Let’s try using this method to modify our list instead:

for-each 循环在幕后使用Iterator,但不那么冗长。然而,如果我们将之前的测试重构为使用Iterator,我们将可以使用额外的方法,例如remove()。让我们尝试使用这个方法来修改我们的列表。

for (Iterator<Integer> iterator = integers.iterator(); iterator.hasNext();) {
    Integer integer = iterator.next();
    if(integer == 2) {

Now we will notice that there is no exception. The reason for this is that the remove() method does not cause a ConcurrentModificationException. It is safe to call while iterating.

现在我们会注意到,没有任何异常。原因是remove() 方法不会引起ConcurrentModificationException。它可以在迭代时安全地调用。

3.2. Not Removing During Iteration


If we want to keep our for-each loop, then we can. It’s just that we need to wait until after iterating before we remove the elements. Let’s try this out by adding what we want to remove to a toRemove list as we iterate:


List<Integer> integers = newArrayList(1, 2, 3);
List<Integer> toRemove = newArrayList();

for (Integer integer : integers) {
    if(integer == 2) {

assertThat(integers).containsExactly(1, 3);

This is another effective way of getting around the problem.


3.3. Using removeIf()


Java 8 introduced the removeIf() method to the Collection interface. This means that if we are working with it, we can use ideas of functional programming to achieve the same results again:

Java 8为Collection接口引入了removeIf()方法。这意味着,如果我们正在使用它,我们可以使用函数式编程的思想来再次实现相同的结果。

List<Integer> integers = newArrayList(1, 2, 3);

integers.removeIf(i -> i == 2);

assertThat(integers).containsExactly(1, 3);

This declarative style offers us the least amount of verbosity. However, depending on the use case, we may find other methods more convenient.


3.4. Filtering Using Streams


When diving into the world of functional/declarative programming, we can forget about mutating collections, instead, we can focus on elements that should be actually processed:


Collection<Integer> integers = newArrayList(1, 2, 3);

List<String> collected = integers
  .filter(i -> i != 2)

assertThat(collected).containsExactly("1", "3");

We’ve done the inverse to our previous example, by providing a predicate for determining elements to include, not exclude. The advantage is that we can chain together other functions alongside the removal. In the example, we use a functional map(), but could use even more operations if we want to.


4. Conclusion


In this article we’ve shown problems that you may encounter if you’re removing items from a collection whilst iterating, and also provided some solutions to negate the issue.


The implementation of these examples can be found over on GitHub. This is a Maven project, so should be easy to run as is.