Est-ce un conteneur Java offre un iterator fail-safe
Question
Voici mon problème:
Ce morceau de code jette un java.util.ConcurrentModificationException
, parce que le Vector
de listeners
est modifié alors qu'il existe un Iterator
pour cette structure de données.
Le java-doc dit que ce conteneur offre seulement un iterator fail-rapide.
Est-il possible d'obtenir un Iterator
sur un conteneur standard comme Vector
ou List
en Java qui me propose un Iterator
, qui ne reçoit pas invalide (est fail-rapide non), s'il y a un élément enlevé pendant cette Iterator
" vit "?
Je devrais avoir le même comportement comme le std::list
en C ++. Là, le iterator est toujours valable même si l'itérateur est de retirer en cours. Que le iterator est fixé à l'élément suivant dans la liste.
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);
}
}
La solution
Pour autant que je sais qu'il n'y a aucun moyen d'ajouter rétroactivement cette capacité à toute mise en œuvre de Collection
par défaut (Iterable
en fait).
Mais il y a des implémentations qui soutiennent ce genre de comportement en ayant des réponses bien définies à des modifications concurrentes tout en réitérant.
Un exemple est CopyOnWriteList
.
Autres conseils
Dans le cas des auditeurs, vous pourriez penser à utiliser java.util.concurrent.CopyOnWriteArrayList
que vous avez généralement beaucoup plus lectures que l'écriture.
un coup d'oeil au paquet java.util.concurrent vous trouverez tout ce dont vous avez besoin.
Une façon paresseuse pour créer un itérateur rapide, fail-safe: prendre une copie de la liste comme un tableau tout verrouillé, et foreach () sur le réseau tout débloqué ... Peut être fait avec tout type de liste
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();
}