Java: Evitar ConcurrentModificationException al usar Iterator

Las colecciones de Java son clases realmente rápidas y muy usables debido a la gran variedad de interfaces que presenta. Esto nos permite tener un amplio abanico de clases a escoger según el tipo de necesidades que se tenga y esto, la verdad, nos facilita mucho el trabajo. Sin embargo, no son nada seguras en cuanto se tienen que realizar inserciones o borrados mientras se recorren, tanto en aplicaciones mono-hilo como mult-hilo, por lo que en este artículo vamos a ver como evitar ConcurrentModificationException al usar Iterator.

Lo más común es sincronizar dicha colección para evitar se sea modificada mientras es iterada a la misma vez (o viceversa) o incluso obtener arrays de valores para recorrerla. El problema que presenta esto para los desarrolladores que piensan en paralelo y están obsesionados con la optimización (como yo 😉 y en el buen sentido) es que ralentiza en exceso nuestras aplicaciones multi-thread.

Buscando una solución más optima y tras investigar un poco sobre su rendimiento (mucho más rápido que sincronizando) he decido probar las clases concurrentes que posee java (java.concurrent.*), especialmente la clase ConcurrentHashMap y los resultandos han sido bastante satisfactorios aunque, a parte de de facilitarte la sincronización de este objeto, es buena idea realizar algunos controles adicionales para evitar posibles valores nulos.

Vamos a ver un ejemplo de uso para concurrencia de listas y de maps:

package es.happyminds.concurrent.example;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public class ThreadSafeExample {
	public static void main(String[] args) {

		List concurrentList = new CopyOnWriteArrayList();

		for (int i = 1; i <= 10; i++) {
			concurrentList.add(String.valueOf(i));
		}

		System.out.println("List size:" + concurrentList.size());

		Iterator it = concurrentList.iterator();
		while (it.hasNext()) {
			String value = it.next();
			System.out.println("List value:" + value);
			if (value.equals("3")) {
				concurrentList.remove("6");
				concurrentList.add("11");
				concurrentList.add("12");
				concurrentList.remove("4");
				concurrentList.remove("9");
			}
		}
		System.out.println("List size:" + concurrentList.size());

		Map<String, String> concurrentMap = new ConcurrentHashMap<String, String>();
		for (int i = 1; i <= 10; i++) {
			concurrentMap.put(String.valueOf(i), String.valueOf(i));
		}

		System.out.println("Map size:" + concurrentMap.size());

		it = concurrentMap.keySet().iterator();
		while (it.hasNext()) {
			String key = it.next();
			System.out.println("Map value:" + concurrentMap.get(key));
			if (key.equals("7")) {
				concurrentMap.remove("8");
				concurrentMap.put("11", "11");
				concurrentMap.put("12", "12");
				concurrentMap.remove("10");
				concurrentMap.remove("4");
			}
		}

		System.out.println("Map size:" + concurrentMap.size());
	}
}

Y la salida es la siguiente:

List size:10
List value:1
List value:2
List value:3
List value:4
List value:5
List value:6
List value:7
List value:8
List value:9
List value:10
List size:9
Map size:10
Map value:1
Map value:7
Map value:null
Map value:5
Map value:6
Map value:3
Map value:9
Map value:12
Map value:2
Map size:9

Como podéis observar, la colección se actualiza dinámicamente mientras se recorre y, aunque no lanza la temida ConcurrentModificationException si que puede presentar valores nulos tal y como comenté y que hay que tener en cuenta en caso de que se haga algún tratamiento con dichos valores mientras se recorre y actualiza.

Espero que os sirva de ayuda.

Happy Minds!!!

Share on FacebookTweet about this on TwitterShare on LinkedInShare on RedditShare on Google+Digg thisShare on TumblrPin on PinterestBuffer this pagePrint this pageEmail this to someone