foreach进不去报错java_Foreach报错

List a = new ArrayList();

2 a.add("1");

3 a.add("2");

4 for (String temp : a) {

5 if("1".equals(temp)){

6 a.remove(temp);

7 }

8 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

此时执行代码,没有问题,但是需要注意,循环此时只执行了一次。具体过程后面去分析。再来看一段会出问题的代码:

48304ba5e6f9fe08f3fa1abda7d326ab.png

List a = new ArrayList();

a.add("1");

a.add("2");

for (String temp : a) {

if("2".equals(temp)){

a.remove(temp);

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

输出为:

Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)at java.util.ArrayList$Itr.next(ArrayList.java:831)at luyudepackage.waitTest.main(waitTest.java:57)

是不是很奇怪?接下来将class文件,反编译下,结果如下

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 List a = new ArrayList();

2 a.add("1");

3 a.add("2");

4 Iterator i$ = a.iterator();

5 do

6 {

7 if(!i$.hasNext())

8 break;

9 String temp = (String)i$.next();

10 if("1".equals(temp))

11 a.remove(temp);

12 } while(true);

48304ba5e6f9fe08f3fa1abda7d326ab.png

几个需要注意的点:

1.foreach遍历集合,实际上内部使用的是iterator。

2.代码先判断是否hasNext,然后再去调用next,这两个函数是引起问题的关键。

3.这里的remove还是list的remove方法。

先去观察下list.remove()方法中的核心方法fastRemove()方法。

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 private void fastRemove(int index) {

2 modCount++;

3 int numMoved = size - index - 1;

4 if (numMoved > 0)

5 System.arraycopy(elementData, index+1, elementData, index,

6 numMoved);

7 elementData[--size] = null; // clear to let GC do its work

8 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

注意第二行,modCount++,此处先不表,下文再说这个参数。

顺路观察下list.add()方法

1 public boolean add(E e) {

2 ensureCapacityInternal(size + 1); // Increments modCount!!

3 elementData[size++] = e;

4 return true;

5 }

注意第二行的注释,说明这个方法也会使modCount++

再去观察下,iterator()方法

1 public Iterator iterator() {

2 return new Itr();

3 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 private class Itr implements Iterator {

2 int cursor; // index of next element to return

3 int lastRet = -1; // index of last element returned; -1 if no such

4 int expectedModCount = modCount;

5

6 public boolean hasNext() {

7 return cursor != size;

8 }

9

10 @SuppressWarnings("unchecked")

11 public E next() {

12 checkForComodification();//万恶之源13 int i = cursor;

14 if (i >= size)

15 throw new NoSuchElementException();

16 Object[] elementData = ArrayList.this.elementData;

17 if (i >= elementData.length)

18 throw new ConcurrentModificationException();

19 cursor = i + 1;

20 return (E) elementData[lastRet = i];

21 }

22

23 public void remove() {

24 if (lastRet < 0)

25 throw new IllegalStateException();

26 checkForComodification();

27

28 try {

29 ArrayList.this.remove(lastRet);

30 cursor = lastRet;

31 lastRet = -1;

32 expectedModCount = modCount;

33 } catch (IndexOutOfBoundsException ex) {

34 throw new ConcurrentModificationException();

35 }

36 }

37

38 final void checkForComodification() {

39 if (modCount != expectedModCount)

40 throw new ConcurrentModificationException();

41 }

42 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

几个需要注意的点:

1.在iterator初始化的时候(也就是for循环开始处),expectedModCount = modCount,猜测是和当时list内部的元素数量有关系(已证实)。

2.当cursor != size的时候,hasNext返回true

3.next()函数的第一行,checkForComodification()这个函数就是报错的原因 这个函数就是万恶之源

4.第39行,mod != expectedModCount 就会抛出ConcurrentModificationException()

接下来分析文章开头的第一个例子,为啥不会报错?

第一个例子执行完第一次循环后,mod = 3 expectedModCount =2 cursor = 1 size = 1  所以程序在执行hasNext()的时候会返回false,所以程序不会报错。

第二个例子执行完第二次循环后,mod = 3 expectdModCount = 2 cursor = 2 size = 1 此时cursor != size 程序认定还有元素,继续执行循环,调用next方法但是此时mod != expectedModCount 所以此时会报错。

道理我们都懂了,再看一个例子

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public static void main(String[] args) throws Exception {

2 List a = new ArrayList();

3 a.add("1");

4 a.add("2");

5 for (String temp : a) {

6 System.out.println(temp);

7 if("2".equals(temp)){

8 a.add("3");

9 a.remove("2");

10 }

11 }

12 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

此时输出为:

1

2

显然,程序并没有执行第三次循环,第二次循环结束,cursor再一次等于size,程序退出循环。

与remove类似,将文章开头的代码中remove替换为add,我们会发现无论是第一个例子还是第二个例子,都会抛出ConcurrentModificationException错误。

原因同上,代码略。

手册上推荐的代码如下

1 Iterator it = a.iterator(); while(it.hasNext()){

2 String temp = it.next(); if(删除元素的条件){

3 it.remove();

4 }

5 }

此时remove是iterator的remove,我们看一下它的源码:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public void remove() {

2 if (lastRet < 0)

3 throw new IllegalStateException();

4 checkForComodification();

5

6 try {

7 ArrayList.this.remove(lastRet);

8 cursor = lastRet; //index of last element returned;-1 if no such

9 lastRet = -1;

10 expectedModCount = modCount;

11 } catch (IndexOutOfBoundsException ex) {

12 throw new ConcurrentModificationException();

13 }

14 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

注意第10行,第8行,所以此时程序不会有之前的问题。

但是手册上推荐的方法,在多线程环境还是有可能出现问题,一个线程执行上面的代码,一个线程遍历迭代器中的元素,同样会抛出CocurrentModificationException。

如果要并发操作,需要对iterator对象加锁。

平时遍历list,然后删除某个元素的时候,如果仅仅删除第一个且删除之后调用break  //代表着此时不会再去执行iterator.next方法 也就不会触发万恶之源

而如果要删除所有的某元素,则会报错,谨记!

Ps再来看一个佐证

48304ba5e6f9fe08f3fa1abda7d326ab.png

public static void main(String[] args) {

ArrayList list = new ArrayList<>();

list.add(1);

list.add(2);

list.add(3);

for(int i : list){

System.out.println(i);

if(i == 2){

list.remove((Object)2);

}

}

}

只是ArrayList是线程不安全的,在被修改后再继续迭代就报错,

modCount是指ArrayList的修改次数,每次add或remove都会自增,

当迭代时,就是将这个modCount暂存在expectedModCount中,

每次获取下一个元素时,都检查下修改次数是否有变动,有变动则不再继续迭代,而是抛出错误ConcurrentModificationException

这样就强制要求在迭代时不能进行remove/add操作,而foreach会编译成迭代,所以foreach时也不能进行remove/add操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值