先看一个代码:
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循环也没有什么问题,如果有不对的地方,欢迎大家指点。