Puede hacer lo que quiera si usa un objeto iterador para repasar los elementos de su conjunto. Puede eliminarlos sobre la marcha y está bien. Sin embargo, eliminarlos mientras está en un bucle for (ya sea "estándar", de cada tipo) le traerá problemas:
Set<Integer> set = new TreeSet<Integer>();
set.add(1);
set.add(2);
set.add(3);
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()) {
Integer setElement = iterator.next();
if(setElement==2) {
iterator.remove();
}
}
for(Integer setElement:set) {
if(setElement==2) {
set.remove(setElement);
}
}
Según el comentario de @ mrgloom, aquí hay más detalles sobre por qué la forma "mala" descrita anteriormente es, bueno ... mala:
Sin entrar en demasiados detalles sobre cómo Java implementa esto, a un alto nivel, podemos decir que la forma "mala" es mala porque está claramente estipulado como tal en los documentos de Java:
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
estipular, entre otros, que (énfasis mío):
" Por ejemplo, generalmente no está permitido que un subproceso modifique una colección mientras otro subproceso está iterando sobre ella. En general, los resultados de la iteración no están definidos en estas circunstancias. Algunas implementaciones de iteradores (incluidas las de toda la colección de propósito general implementaciones proporcionadas por el JRE) pueden optar por lanzar esta excepción si se detecta este comportamiento "(...)
" Tenga en cuenta que esta excepción no siempre indica que un objeto ha sido modificado simultáneamente por un subproceso diferente. Si un solo subproceso emite una secuencia de invocaciones de método que viola el contrato de un objeto, el objeto puede lanzar esta excepción. Por ejemplo, si un hilo modifica una colección directamente mientras está iterando sobre la colección con un iterador rápido, el iterador lanzará esta excepción ".
Para entrar más en detalles: un objeto que se puede usar en un bucle forEach necesita implementar la interfaz "java.lang.Iterable" (javadoc aquí ). Esto produce un iterador (a través del método "Iterador" que se encuentra en esta interfaz), que se crea una instancia bajo demanda, y contendrá internamente una referencia al objeto Iterable desde el cual fue creado. Sin embargo, cuando se usa un objeto Iterable en un bucle forEach, la instancia de este iterador está oculta para el usuario (no puede acceder a él de ninguna manera).
Esto, junto con el hecho de que un iterador tiene bastante estado, es decir, para hacer su magia y tener respuestas coherentes para sus métodos "next" y "hasNext", necesita que el objeto de respaldo no sea cambiado por otra cosa que no sea el iterador. mientras está iterando, lo hace para que arroje una excepción tan pronto como detecte que algo cambió en el objeto de respaldo mientras está iterando sobre él.
Java llama a esta iteración "a prueba de fallas": es decir, hay algunas acciones, generalmente aquellas que modifican una instancia Iterable (mientras un Iterador está iterando sobre ella). La parte de "falla" de la noción de "falla rápida" se refiere a la capacidad de un iterador para detectar cuándo ocurren tales acciones de "falla". La parte "rápida" de la "falla rápida" (y, que en mi opinión debería llamarse "mejor esfuerzo rápido"), terminará la iteración a través de ConcurrentModificationException tan pronto como pueda detectar que una acción "falla" ocurrir.