ArrayList循环删除会不会出现什么问题?

今天在网上看到一道面试题,感觉很有趣。
既然都这么问了,那肯定得说有问题啊。

那么ArrayList循环删除到底会不会出现什么问题?实践出真理》》》

先准备数据:

    public  static  void  main(String[] args){
       ArrayList<String> arrayList = new ArrayList<>();
       arrayList.add("sss");
       arrayList.add("sss");
       arrayList.add("xxx");
        arrayList.add("aaa");
       arrayList.add("bbb");
       arrayList.add("xxx");

       arrayList = deleteTemp(arrayList,"sss");

       System.out.println(Arrays.toString(arrayList.toArray()));

    }

正向循环删除

public static ArrayList<String> deleteTemp(ArrayList arrayList , String temp){
        for (int i = 0 ; i < arrayList.size();i++){
            if (arrayList.get(i).equals(temp)){
                arrayList.remove(i);
            }
        }
        return  arrayList ;
   }

结果:

   [sss, xxx, aaa, bbb, xxx]

是不是很神奇,竟然有一个“有一个sss”没有被删除。

原因:
这是因为ArrayList是个动态数组。
当删除了第一个sss的时候,那时候的i是0。由于移除了一个元素,剩下的元素就会向前移一位,然后很奇妙的事情就发生了
第二个sss本来在第1位,但是第0位没有了,向前移动,此时第二个sss的下标就变成了0,然后然后这个本该被删除的sss就被错过了。

逆向循环删除

 public static ArrayList<String> deleteTemp(ArrayList arrayList , String temp){
   for (int i = arrayList.size()-1 ; i>=0; i--){
            if (arrayList.get(i).equals(temp)){
                arrayList.remove(i);
            }
        }
        return  arrayList ;
}

结果

[xxx, aaa, bbb, xxx]

删除成功!为什么倒过来删就成功了呢??
原因:
因为是从后面开始比遍历数组,后面遍历先碰到第一个符合的元素就会删除,但是并不会影响前面符合元素的位置。

附上一张for循环删除的ArraList数组元素的移动的示意图,见图就能知道上面的原因了
附上一张for循环删除的ArraList数组元素的移动的示意图,见图就能知道上面的原因了

使用Iterator循环正序删除

 public static ArrayList<String> deleteTemp(ArrayList arrayList , String temp){
	 Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            if (iterator.next().equals(temp)){
                arrayList.remove(iterator.next());
            }
        }
        return  arrayList ;
}

结果:

com.neu.deletearraylist.DeleteArrayList
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.neu.deletearraylist.DeleteArrayList.deleteTemp(DeleteArrayList.java:48)
	at com.neu.deletearraylist.DeleteArrayList.main(DeleteArrayList.java:19)

结果报错,惊不惊喜,意不意外?
这就得跑去看源码了
查看ArrayList 的remove方法,看ArrayList 的remove方法有两个,都是重构了的方法,第一个remove方法是根据下标移除元素的,第二个remove方法是直接移除元素的,我们重点就看这个remove(object o)方法。

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

这里又调用了 fastRemove(index)方法

  private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

注意到:
有一个变量modCount++,这个modCount是记录数组修改的次数,还有一个变量是expectedModCount,这个expectedModCount是final修饰的,它的初始值等于modCount。

使用Iterator循环就会涉及到涉及到iterator迭代。ArrayList.java里面有一个next()的方法:

public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

这个方法又调用了一个checkForComodification()方法:

 final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

这个方法很简单,当modCount != expectedModCount时就抛出异常

看了那么久现在终于有点眉目了吧
ArrayList的remov(object o)方法,这个方法又调用 fastRemove(index)方法,这个方法里面,modCount++,modCount的值变了,然后触发iterator迭代调用next()方法,这个next()方法就会调用checkForComodification()方法,这个方法一比较modCount 和expectedModCount就会发现不它们一样,就会报错了。

追加两点:
多线程反向遍历删除是没有问题的。
用Iterator中的remove方法也是没有问题的。

如何验证还请大家自行去尝试了或者去看其他大神的博客啦,因为小编的线程和迭代学得不是很到位。

有问题欢迎留言讨论哦。(_

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值