Site Search:

HiddenIterator.java

<Back


The standard way to iterate a Collection is with an Iterator, either explicitly or through the for-each loop. If the collection has changed since iteration began, the iterator will throw the unchecked ConcurrentModificationException.

Guarding the iterator with a correct lock is easy, but the challenge is to find all the hidden iterators that need to be guarded.

In the following example, the System.out.println has a hidden iterator -- string concatenation gets turned by the compiler into a call to StringBuilder.append(Object), which in turn invokes the HashSet's toString method, which iterates the collection and calls toString on each element. As a result, while main thread is printing, the other thread could be removing elements, so the main thread sometimes throws ConcurrentModificationException.


import java.util.HashSet;
import java.util.Set;

public class HiddenIterator {
    private final Set<Integer> set = new HashSet<Integer>();

    public synchronized void add(Integer i) {
        set.add(i);
    }

    public synchronized void remove(Integer i) {
        set.remove(i);
    }

    public void addTenThings() {
        for (int i = 0; i < 10; i++)
            add(i);
        System.out.println("DEBUG: added ten elements to " + set); //println called set.toString(), which used the iterator
        //new HashSet<Integer>().toString();  
    }
    
    public static void main(String[] args) {
        HiddenIterator h = new HiddenIterator();
        h.addTenThings();
        new Thread(new Runnable(){

            @Override
            public void run() {
                for(int i = 0; i < 5; i++) {
                    h.remove(i);
                }
            }}).start();
        h.addTenThings();
    }

}