您的位置 首页 java

java加强for循环陷阱

1、 for循环 遍历list

这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的 索引 也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。

2、增强for循环

这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用 break 跳出,则不会触发报错。

原因1:

remove()方法和remove(Object o)方法之间是存在差异的。查看源码,可以看到remove()方法里有一个“expectedModCount = modCount;”语句;而在remove(Object o)方法是这样的“modCount++;”它没有对expectedModCount做处理,导致在checkForComodification()方法判断“expectedModCount == modCount”时出错。所以不管在什么时候,只要你调用了remove(Object o)方法,然后又调用了next()方法,都一定会报ConcurrentModificationException这个异常的。可以尝试一下将remove()方法摆在next()方法前,是可以用的。希望能帮助到大家。

原因2:

对于for(), JAVA 虚拟机会将其翻译成 iterator 迭代器,java编译器中含有:hasNext()函数,而hasNext()函数中含有:size()函数,这意味着list数组的大小是动态生成的,它是使用index!=size()来判定是否有下一个元素的,理论上只有最后元素才回返回 false ,其它元素都会返回true,一旦返回了true就会调用next,这样在调用next的过程中就会调用concurrentExceptions,在for()中,不容许调用remove()函数。这也就解释了,删除倒数第二个数,不会导致报错,因为在判断倒数第二个数的时候,index!=size直接返回false,这样没有next,也就不存在next的调用了。

3、iterator遍历

上述代码运行正确的原因:

Iterator是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java .util.ConcurrentModificationException异常。

所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。。

总结:

(1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题。

(2)循环删除list中多个元素的,应该使用迭代器iterator方式。JAVA中循环遍历list有三种方式for循环、增强for循环(也就是常说的 foreach 循环)、iterator遍历。

文章来源:智云一二三科技

文章标题:java加强for循环陷阱

文章地址:https://www.zhihuclub.com/184490.shtml

关于作者: 智云科技

热门文章

网站地图