1301 Stimmen

Iteration durch eine Sammlung, Vermeidung von ConcurrentModificationException beim Entfernen von Objekten in einer Schleife

Wir alle wissen, dass Sie Folgendes nicht tun können, weil ConcurrentModificationException :

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

Aber das funktioniert offenbar manchmal, aber nicht immer. Hier ist ein spezieller Code:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

Das führt natürlich dazu, dass:

Exception in thread "main" java.util.ConcurrentModificationException

Auch wenn mehrere Threads dies nicht tun. Wie auch immer.

Was ist die beste Lösung für dieses Problem? Wie kann ich ein Element aus der Sammlung in einer Schleife entfernen, ohne diese Ausnahme auszulösen?

Ich verwende auch eine willkürliche Collection hier, nicht unbedingt ein ArrayList Sie können sich also nicht auf get .

1 Stimmen

Hinweis an die Leser: Lesen Sie unbedingt die docs.oracle.com/javase/tutorial/collections/interfaces/ kann es einen einfacheren Weg geben, um das zu erreichen, was Sie tun wollen.

0voto

Alferd Nobel Punkte 1845

Am Ende habe ich das hier bekommen ConcurrentModificationException während der Iteration der Liste mit stream().map() Methode. Allerdings ist die for(:) hat beim Iterieren und Ändern der Liste keine Ausnahme ausgelöst.

Hier ist ein Codeschnipsel, falls es jemandem hilft: Hier iteriere ich über eine ArrayList<BuildEntity> und sie mit list.remove(obj) zu ändern

 for(BuildEntity build : uniqueBuildEntities){
            if(build!=null){
                if(isBuildCrashedWithErrors(build)){
                    log.info("The following build crashed with errors ,  will not be persisted -> \n{}"
                            ,build.getBuildUrl());
                    uniqueBuildEntities.remove(build);
                    if (uniqueBuildEntities.isEmpty()) return  EMPTY_LIST;
                }
            }
        }
        if(uniqueBuildEntities.size()>0) {
            dbEntries.addAll(uniqueBuildEntities);
        }

0voto

SM. Hosseini Punkte 88

Wenn Sie HashMap verwenden, können Sie in neueren Versionen von Java (8+) jede der 3 Optionen auswählen:

public class UserProfileEntity {
    private String Code;
    private String mobileNumber;
    private LocalDateTime inputDT;
    // getters and setters here
}
HashMap<String, UserProfileEntity> upMap = new HashMap<>();

// remove by value
upMap.values().removeIf(value -> !value.getCode().contains("0005"));

// remove by key
upMap.keySet().removeIf(key -> key.contentEquals("testUser"));

// remove by entry / key + value
upMap.entrySet().removeIf(entry -> (entry.getKey().endsWith("admin") || entry.getValue().getInputDT().isBefore(LocalDateTime.now().minusMinutes(3)));

-1voto

Nurlan Punkte 612

Für den Fall ArrayList:remove(int index) - if(index ist die Position des letzten Elements) vermeidet es ohne System.arraycopy() und nimmt sich dafür keine Zeit.

Die Zeit für die Arraykopie erhöht sich, wenn (der Index sinkt), übrigens auch die Elemente der Liste sinken!

Die beste Methode zum Entfernen ist, die Elemente in absteigender Reihenfolge zu entfernen: while(list.size()>0)list.remove(list.size()-1); //dauert O(1) while(list.size()>0)list.remove(0); //dauert O(faktoriell(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
  • for index Schleife: 1090 msec
  • für desc index: 519 msec--das Beste
  • für Iterator: 1043 msec

-2voto

Firas Chebbah Punkte 389

Können Sie auch verwenden Rekursion

Die Rekursion in Java ist ein Prozess, bei dem sich eine Methode ständig selbst aufruft. Eine Methode in Java, die sich selbst aufruft, wird rekursive Methode genannt.

-3voto

ajax333221 Punkte 11034

Dies ist vielleicht nicht der beste Weg, aber für die meisten kleinen Fälle sollte dies akzeptabel sein:

"Erstellen Sie ein zweites leeres Array und fügen Sie nur diejenigen hinzu, die Sie behalten wollen".

Ich erinnere mich nicht, woher ich das gelesen habe... der Gerechtigkeit halber werde ich dieses Wiki in der Hoffnung erstellen, dass es jemand findet oder einfach nur, um keine Anerkennung zu verdienen, die ich nicht verdiene.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X