先看一個代碼:
public class ArrayListForeach {
public static void main(String[] args) {
removeListElement1(); // 沒有問題
removeListElement2(); // ConcurrentModificationException異常
}
public static void removeListElement1() {
List<String> arrayList = new ArrayList<String>();
arrayList.add("1");
arrayList.add("2");
for (String s : arrayList) {
if ("1".equals(s)) {
arrayList.remove(s);
}
}
arrayList.stream().forEach(System.out::println);
}
public static void removeListElement2() {
List<String> arrayList = new ArrayList<String>();
arrayList.add("2");
arrayList.add("1");
for (String s : arrayList) {
if ("1".equals(s)) {
arrayList.remove(s);
}
}
arrayList.stream().forEach(System.out::println);
}
}
public static void removeListElement4() {
List<String> arrayList = new ArrayList<String>();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
for (String s : arrayList) {
if ("1".equals(s)) {
arrayList.remove(s);
}
}
arrayList.stream().forEach(System.out::println);
}
當有三個元素時候,刪除還是會拋異常,
爲什麼會這樣呢?
foreach的本質就是使用的迭代器Iterator,所有的Collection集合類都會實現Iterable接口。
看一下Iterator代碼,
public Iterator<E> iterator() { return new Itr(); }
再進到new itr裏面看看:
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
這就是源碼部分了,迭代器的本質就是先調用hasNext()判斷是否有下一個元素存在,然後調用next()取到下一個元素。
再看看上面示例代碼:
上面arraylist1爲什麼能remove成功呢,其實它只循環了一次,所以成功了。
因爲它在remove元素1之後,它的size - 1變成1,然後Itr內部的cursor變量由0變成1
此時1 = 1,循環結束,所以成功了。
arraylist2爲什麼remove失敗呢,因爲他在循環第二次的時候,也remove成功了,但是第三次判斷next的時候cursor的值爲2導致不等於現在的size 1,所以執行了next方法,最重要的來了,之前remove的操作導致ArrayList的modCount值加1,然後Itr類中的expectedModCount保持不變,所以會拋出異常。所以會看到上面的異常信息。
list的remove()方法:
所以,要對List進行刪除的時候,可以用下面的迭代器方式:
public static void removeListElement3(){
List<String> arrayList = new ArrayList<String>();
arrayList.add("2");
arrayList.add("1");
Iterator<String> iterator = arrayList.iterator();
while(iterator.hasNext()){
String item = iterator.next();
if("1".equals(item)){
iterator.remove();
}
}
arrayList.stream().forEach(System.out::println);
}
迭代器自己的remove()方法,每次讓expectedModCount = modCount,當然在併發情況下,還得加索,不然還是會拋異常。
public static void removeListElement5() {
List<String> arrayList = new ArrayList<String>();
arrayList.add("2");
arrayList.add("1");
// for (String s : arrayList) {
// if ("1".equals(s)) {
// arrayList.remove(s);
// }
// }
for (int i =0;i<arrayList.size();i++){
if ("1".equals(arrayList.get(i))) {
arrayList.remove(arrayList.get(i));
}
}
arrayList.stream().forEach(System.out::println);
}
這種普通for循環也沒有什麼問題,如果有不對的地方,歡迎大家指點。