Właściwie to zależy od wersji jdk, której używasz. W JDK6, większość klasy zbierania dziedziczą metodę removeAll z klasy AbstractCollection:
public boolean removeAll(Collection<?> c) {
boolean modified = false;
Iterator<?> e = iterator();
while (e.hasNext()) {
if (c.contains(e.next())) {
e.remove();
modified = true;
}
}
return modified;
}
Więc w jakimś scenariuszu, będzie to powodować CME (ConcurrentModificationException
), na przykład:
ArrayList<Integer> it = new ArrayList<Integer>();
it.add(1);
it.add(2);
it.add(3);
List<Integer> sub = it.subList(0,5);
it.removeAll(sub);
jednak w jdk8 większość kolekcji ma własną implementację removeAll, taką jak ArrayList. Klasa ArrayList ma swoją własną metodę removeAll, co nazywamy prywatna metoda batchRemove:
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
Ten użyciem pętli iteracyjne nad bazowego tablicy oraz zmianę zmiennej modCount tylko raz po zakończeniu tej metody, podczas iteracja podlisty (wywołanie przez c. zawiera (elementData [r])), modCount nie zmienia się, więc nie zostanie rzucona CME.
Jaki jest możliwy powód takiego postępowania zamiast opcji 'op.clear()'? –
Tak, może; możesz iterować na tej samej kolekcji, z której usuwasz, co może prowadzić do 'ConcurrentModificationException'. – fge
Proszę zobaczyć moją modyfikację. – isoman