前言:
for循环可以删除集合元素吗,往往我们得到的答案有时候就是不可以,安全起见,要迭代器,包括我在阿里的开发规范里也写了这么一句话, 不要在 foreach
循环里进行元素的 remove / add
操作。remove
元素请使用 iterator
方式,如果并发操作,需要对 iterator
对象加锁
依然记得刚来第三天写个接口我就for循环
内删除元素,当时很沙雕,恰好又被代码走查看到了,尴尬的我挖了个洞将for改成了迭代器方式遍历,这两天看个大佬的代码,他就是for循环并remove
其中元素,我开心的以为发现了一个bug,嗯,再往下看不对,这代码妙啊,百度了一下,有了这篇文章 下面我们通过几个例子以及分析源码的方式来看看问题,nice
问题一
List<String> list = new ArrayList();
list.add("111");
list.add("222");
list.add("222");
list.add("333");
list.add("222");
list.add("555");
//list.stream().forEach(System.out::println);
for(int i = 0;i < list.size();i++){
if(StrUtil.equals("222",list.get(i))){
list.remove(i);
}}
我们先看下上面这个用例,这个结果是啥呢?是111 222 333 555
,咦,明明等于222的移除了啊,怎么没移掉,而且还没报错,通常我们移除元素会报错呀,其实这种for方法在我们循环遍历的时候list.remove(i)
;会删除对应的元素不会报错,但是呢,删除的元素位置会空出来,后面的元素会往前移一位,这样如果有两个元素的位置是连续的话,那么后面这个元素是不会进行判断的,这样就不会符合我们的分析场景的,
我们按代码顺序翻一下,索引在范围内,则获取remove
的元素,然后将list的元素大小减一,如果还存在,就进行元素的copy,
从源数组的index+1
位置开始要复制的数组元素的数量numMoved
,到目标数组的指定位置,然后通过GC将最后一个位置内存回收,哦。原来是这样的,至于说的报错我们下面在分析
问题二
for (String ll : list) {
if(StrUtil.equals(ll,"333")){
list.remove(ll);
}}
如上代码,当我们使用foreach的时候我们需要remove的是一个对象,而不是for时的下标,这里会报错java.util.ConcurrentModificationException,这就是我们说的报错了,我先把结果说了吧,这里我们删除元素的话其实并不会报错,报错的是for循环哪里,在你remove后下一次遍历的时候才会报错,报异常的方法是java.util.ArrayList$Itr
.checkForComodificatio
n,一看就是方法里的迭代器报错
- 一个是删除后元素位置前挪了导致连续相等的元素判断不到
- 一个是删除元素后改动的次数变得和期望变动的次数不一样了导致的这些异常信息
for(int i = list.size()-1;i>=0;i--){
if(StrUtil.equals("222",list.get(i))){
String remove = list.remove(i);
System.out.println("shanchu"+ remove);
}}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。