elemento - imprimir linkedlist java
Iterando a través de una colección, evitando ConcurrentModificationException al modificar(eliminar) en bucle (22)
La mejor manera (recomendada) es el uso del paquete java.util.Concurrent. Al utilizar este paquete, puede evitar fácilmente esta excepción. refiera el código modificado
public static void main(String[] args) {
Collection<Integer> l = new CopyOnWriteArrayList<Integer>();
for (int i=0; i < 10; ++i) {
l.add(new Integer(4));
l.add(new Integer(5));
l.add(new Integer(6));
}
for (Integer i : l) {
if (i.intValue() == 5) {
l.remove(i);
}
}
System.out.println(l);
}
Todos sabemos que no puedes hacer esto:
for (Object i : l) {
if (condition(i)) {
l.remove(i);
}
}
ConcurrentModificationException
etc ... esto parece funcionar a veces, pero no siempre. Aquí hay un código específico:
public static void main(String[] args) {
Collection<Integer> l = new ArrayList<Integer>();
for (int i=0; i < 10; ++i) {
l.add(new Integer(4));
l.add(new Integer(5));
l.add(new Integer(6));
}
for (Integer i : l) {
if (i.intValue() == 5) {
l.remove(i);
}
}
System.out.println(l);
}
Esto, por supuesto, resulta en:
Exception in thread "main" java.util.ConcurrentModificationException
... aunque varios hilos no lo están haciendo ... De todos modos.
¿Cuál es la mejor solución a este problema? ¿Cómo puedo eliminar un elemento de la colección en un bucle sin lanzar esta excepción?
También estoy usando una Collection
arbitraria aquí, no necesariamente una ArrayList
, por lo que no puedes confiar en get
.
Con Eclipse Collections (anteriormente GS Collections ), el método removeIf
definido en MutableCollection funcionará:
MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);
Con la sintaxis de Java 8 Lambda esto se puede escribir de la siguiente manera:
MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);
La llamada a Predicates.cast()
es necesaria aquí porque se agregó un método removeIf
predeterminado en la interfaz java.util.Collection
en Java 8.
Nota: Soy un comendador de Eclipse Collections .
Con Java 8 puedes usar el nuevo método removeIf
. Aplicado a tu ejemplo:
Collection<Integer> coll = new ArrayList<Integer>();
//populate
coll.removeIf(i -> i.intValue() == 5);
Con un tradicional para bucle.
ArrayList<String> myArray = new ArrayList<>();
for (int i = 0; i < myArray.size(); ) {
String text = myArray.get(i);
if (someCondition(text))
myArray.remove(i);
else
i++;
}
Ejemplo de modificación de la colección segura de hilos:
public class Example {
private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());
public void removeFromQueue() {
synchronized (queue) {
Iterator<String> iterator = queue.iterator();
String string = iterator.next();
if (string.isEmpty()) {
iterator.remove();
}
}
}
}
En el caso de ArrayList: remove (int index) : si (el índice es la posición del último elemento), evita System.arraycopy()
y no requiere tiempo para esto.
el tiempo de arraycopy aumenta si (el índice disminuye), por cierto, los elementos de la lista también disminuyen!
la mejor manera efectiva de eliminar es eliminando sus elementos en orden descendente: while(list.size()>0)list.remove(list.size()-1);
// toma O (1) while(list.size()>0)list.remove(0);
// toma O (factorial (n))
//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
Integer integer = rdm.nextInt();
ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion
// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++)
if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion
// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--)
if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion
// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
- para bucle de índice: 1090 ms
- Índice de desc: 519 ms. --- el mejor
- para iterador: 1043 ms
En tales casos, un truco común es (¿era?) Retroceder:
for(int i = l.size() - 1; i >= 0; i --) {
if (l.get(i) == 5) {
l.remove(i);
}
}
Dicho esto, estoy más que contento de tener mejores formas en Java 8, por ejemplo, removeIf
o filter
on streams.
Esto funciona:
Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
if (iter.next().intValue() == 5) {
iter.remove();
}
}
Asumí que, dado que un bucle foreach es azúcar sintáctica para la iteración, usar un iterador no ayudaría ... pero le ofrece esta funcionalidad .remove()
.
Haga una copia de la lista existente y repita sobre la nueva copia.
for (String str : new ArrayList<String>(listOfStr))
{
listOfStr.remove(/* object reference or index */);
}
La gente está afirmando que no se puede eliminar de una colección que está iterando por un bucle foreach. Solo quería señalar que es técnicamente incorrecto y describir exactamente (sé que la pregunta del OP es tan avanzada que se debe obviar saber esto) el código detrás de esa suposición:
for (TouchableObj obj : untouchedSet) { // <--- This is where ConcurrentModificationException strikes
if (obj.isTouched()) {
untouchedSet.remove(obj);
touchedSt.add(obj);
break; // this is key to avoiding returning to the foreach
}
}
No es que no se pueda eliminar de la Colletion
iterada, sino que no se puede continuar con la iteración una vez que lo haga. De ahí la break
en el código anterior.
Le pido disculpas si esta respuesta es un caso de uso un tanto especializado y más adecuado para el thread original del que llegué aquí, este se marca como un duplicado (a pesar de que este hilo aparece más matizado) de este y se bloquea.
La misma respuesta que Claudius con un bucle for:
for (Iterator<Object> it = objects.iterator(); it.hasNext();) {
Object object = it.next();
if (test) {
it.remove();
}
}
Puede iterar la lista usando for-loop y necesita llamar a list.remove (0). Debe codificar el índice y eliminar el parámetro del índice con cero. Véase también esta respuesta :
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
int list_size = list.size();
for (int i = 0; i < list_size; i++) {
list.remove(0);
}
Puede que esta no sea la mejor manera, pero para la mayoría de los casos pequeños, esto debería ser aceptable:
"crea una segunda matriz vacía y agrega solo las que quieras conservar"
No recuerdo de dónde leí esto desde ... por razones de justicia haré este wiki con la esperanza de que alguien lo encuentre o simplemente para no obtener una reputación que no merezco.
Puede usar el iterador directamente como lo mencionó, o bien mantener una segunda colección y agregar cada elemento que desee eliminar a la nueva colección, luego eliminar todo al final. Esto le permite seguir usando la seguridad de tipos del bucle for-each a costa de un mayor uso de memoria y tiempo de CPU (no debería ser un gran problema a menos que tenga listas realmente grandes o una computadora muy vieja)
public static void main(String[] args)
{
Collection<Integer> l = new ArrayList<Integer>();
Collection<Integer> itemsToRemove = new ArrayList<Integer>();
for (int i=0; i < 10; ++i) {
l.add(new Integer(4));
l.add(new Integer(5));
l.add(new Integer(6));
}
for (Integer i : l)
{
if (i.intValue() == 5)
itemsToRemove.add(i);
}
l.removeAll(itemsToRemove);
System.out.println(l);
}
Sé que esta pregunta es demasiado antigua para ser acerca de Java 8, pero para aquellos que usan Java 8, puedes usar removeIf () fácilmente:
Collection<Integer> l = new ArrayList<Integer>();
for (int i=0; i < 10; ++i) {
l.add(new Integer(4));
l.add(new Integer(5));
l.add(new Integer(6));
}
l.removeIf(i -> i.intValue() == 5);
Tengo una sugerencia para el problema anterior. No hay necesidad de lista secundaria o tiempo extra. Por favor encuentre un ejemplo que haga lo mismo pero de una manera diferente.
//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
Object r = list.get(index);
if( state ) {
list.remove(index);
index = 0;
continue;
}
index += 1;
}
Esto evitaría la excepción de concurrencia.
Un ListIterator
permite agregar o eliminar elementos de la lista. Supongamos que tiene una lista de objetos de Car
:
List<Car> cars = ArrayList<>();
// add cars here...
for (ListIterator<Car> carIterator = cars.listIterator(); carIterator.hasNext(); )
{
if (<some-condition>)
{
carIterator().remove()
}
else if (<some-other-condition>)
{
carIterator().add(aNewCar);
}
}
Ya que la pregunta ya ha sido respondida, es decir, la mejor manera es usar el método de eliminación del objeto iterador, me gustaría entrar en los detalles del lugar donde se produce el error "java.util.ConcurrentModificationException"
.
Cada clase de colección tiene una clase privada que implementa la interfaz Iterator y proporciona métodos como next()
, remove()
y hasNext()
.
El código para el próximo se ve algo como esto ...
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch(IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
Aquí el método checkForComodification
se implementa como
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Entonces, como puede ver, si intenta explícitamente eliminar un elemento de la colección. Da como resultado que modCount
se diferencie de expectedModCount
, lo que da como resultado la excepción ConcurrentModificationException
.
ConcurrentHashMap o ConcurrentLinkedQueue o ConcurrentSkipListMap puede ser otra opción, ya que nunca lanzarán ninguna ConcurrentModificationException, incluso si elimina o agrega un elemento.
Iterator.remove()
es seguro, puedes usarlo así:
List<String> list = new ArrayList<>();
// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
// Iterator<String> iterator = list.iterator();
// while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String string = iterator.next();
if (string.isEmpty()) {
// Remove the current element from the iterator and the list.
iterator.remove();
}
}
Tenga en cuenta que Iterator.remove()
es la única forma segura de modificar una colección durante la iteración; el comportamiento no se especifica si la colección subyacente se modifica de otra manera mientras la iteración está en curso.
Fuente: docs.oracle.com/javase/tutorial/collections/interfaces/…
Y de manera similar, si tiene un ListIterator
y desea agregar elementos, puede usar ListIterator#add
, por la misma razón que puede usar Iterator#remove
- está diseñado para permitirlo.
En su caso, intentó eliminar de una lista, pero se aplica la misma restricción si trata de put
en un Map
mientras itera su contenido.
Collection<Integer> l = new ArrayList<Integer>();//Do the collection thing...
l.removeIf(i -> i == 5); //iterates through the collection and removes every occurence of 5
Las expresiones Lambda y los métodos de colección en Jdk 8 vienen en Handy y agregan un poco de azúcar sintáctico 😊.
El método removeIf
recorre la colección y filtra con Predicate. Un predicado es la función de un argumento que devuelve un valor booleano ... Al igual que el valor boolean _bool = (str) -> str.equals("text");
for (Integer i : l)
{
if (i.intValue() == 5){
itemsToRemove.add(i);
break;
}
}
El problema es después de eliminar el elemento de la lista si omite la llamada interna iterator.next (). ¡aún funciona! Aunque no me propongo escribir código como este, me ayuda a entender el concepto que hay detrás :-)
¡Aclamaciones!