views:

493

answers:

4

Here is my problem:

This piece of code throws a java.util.ConcurrentModificationException, because the Vector listeners is modified while there exists an Iterator for this data structure. The java-doc says that this container offers only a fail-fast iterator.

Is there a possibility to get an Iterator over a standard container like Vector or List in Java that offers me an Iterator, that does not get invalid (is not fail-fast), if there is an element removed during that Iterator "lives"?

I should have the same behavior like the std::list in C++. There the iterator is always valid even if the current iterator is remove. Than the iterator is set to the next element in the list.

public class ClientHandle {
private final Vector<ClientHandleListener> listeners = new Vector<ClientHandleListener>();


public synchronized void  addListener(ClientHandleListener chl) {
    listeners.add(chl);
}

public synchronized void  removeListener(ClientHandleListener chl) {
    listeners.remove(chl); 
}

private void fireConnectionClosed() {
    final ClientHandle c = this;

    final Iterator<ClientHandleListener> it = listeners.iterator();
    new Thread(){
        @Override
        public void run() {
            while (it.hasNext()) {
                it.next().connectionClosed(c); //FIXME the iterator gets modified 
            }
            };
    }.start();
}}

public class ClientHandlePool implements ClientHandleListener, TaskManagerListener {

        /*...*/
    public synchronized void  removeClientHandle(ClientHandle ch) {
                //here the listeners Vector from the ClientHandle gets modified
        ch.removeListener(this); 
        ch.removeListener(currentListener);
        clientHandles.remove(ch);
    }

    @Override
    public void connectionClosed(ClientHandle ch) {
        removeClientHandle(ch);
    }
}
+2  A: 

have a look at java.util.concurrent package you will find everything you need.

Paul Whelan
+6  A: 

In case of listeners, you might think about using java.util.concurrent.CopyOnWriteArrayList as you typically have way more reads than writes.

sfussenegger
+7  A: 

As far as I know there's no way to retroactively add that ability to any default Collection implementation (Iterable in fact).

But there are implementations that support that kind of behaviour by having well-defined responses to concurrent modification while iterating.

One example is the CopyOnWriteList.

Joachim Sauer
+1: CopyOnWriteList is what I was going to suggest.
Software Monkey
+1 thanks. that solved my problem
nutario
A: 

A lazy way to create a fast, fail-safe iterator: take a copy of the list as an array while locked, and foreach() over the array while unlocked... Can be done with any type of List

private void fireConnectionClosed() {
   final ClientHandle c = this;

   final ClientHandleListener[] listenersArr;
   synchronized(this) {
       listenersArr=listeners.toArray(new ClientHandleListener[0]);
   }
   new Thread(){
       @Override
       public void run() {
          for(ClientHandleListener listener : listenersArr )
              listener.connectionClosed(c);
          }
       };
   }.start();
}
CuriousPanda