当要删除ArrayList里面的某个元素,一不注意就容易出bug。今天就给大家说一下在ArrayList循环遍历并删除元素的问题。首先请看下面的例子:

import java.util.ArrayList;
public class ArrayListRemove
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("c");
remove(list); for (String s : list)
{
System.out.println("element : " + s);
}
} public static void remove(ArrayList<String> list)
{ }
}

常见错误写法:

一:

public static void remove(ArrayList<String> list)
{
for (int i = 0; i < list.size(); i++)
{
String s = list.get(i);
if (s.equals("b"))
{
list.remove(s);
}
}
}

结果:第二个“b”的字符串没有删掉。

二:

public static void remove(ArrayList<String> list)
{
for (String s : list)
{
if (s.equals("b"))
{
list.remove(s);
}
}
}

结果:这种for-each写法会报出并发修改异常:java.util.ConcurrentModificationException。

错误原因:先看下ArrayList中的remove方法,看入参为Object的remove方法是怎么实现的:

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;
}

执行路径会到else路径下最终调用faseRemove方法:

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; // Let gc do its work
}

可以看到会执行System.arraycopy方法,导致删除元素时涉及到数组元素的移动。针对错误写法一,在遍历第一个字符串b时因为符合删除条件,所以将该元素从数组中删除,并且将后一个元素移动(也就是第二个字符串b)至当前位置,导致下一次循环遍历时后一个字符串b并没有遍历到,所以无法删除。针对这种情况可以倒序删除的方式来避免:

public static void remove(ArrayList<String> list)
{
for (int i = list.size() - 1; i >= 0; i--)
{
String s = list.get(i);
if (s.equals("b"))
{
list.remove(s);
}
}
}

因为数组倒序遍历时即使发生元素删除也不影响后序元素遍历。

实例二的错误原因。产生的原因却是foreach写法是对实际的Iterable、hasNext、next方法的简写,问题同样处在上文的fastRemove方法中,可以看到第一行把modCount变量的值加一,但在ArrayList返回的迭代器:

public Iterator<E> iterator() {
return new Itr();
}

这里返回的是AbstractList类内部的迭代器实现private class Itr implements Iterator,看这个类的next方法:

public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}

第一行checkForComodification方法:

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

这里会做迭代器内部修改次数检查,因为上面的remove(Object)方法修改了modCount的值,所以才会报出并发修改异常。要避免这种情况的出现则在使用迭代器迭代时(显示或for-each的隐式)不要使用ArrayList的remove,改为用Iterator的remove即可。

public static void remove(ArrayList<String> list)
{
Iterator<String> it = list.iterator();
while (it.hasNext())
{
String s = it.next();
if (s.equals("b"))
{
it.remove();
}
}
}

【Java】List遍历时删除元素的正确方式的更多相关文章

  1. 【原理探究】女朋友问我ArrayList遍历时删除元素的正确姿势是什么?

    简介 我们在项目开发过程中,经常会有需求需要删除ArrayList中的某个元素,而使用不正确的删除方式,就有可能抛出异常.或者在面试中,会遇到面试官询问遍历时如何正常删除元素.所以在本篇文章中,我们会 ...

  2. java list集合遍历时删除元素

    转: java list集合遍历时删除元素 大家可能都遇到过,在vector或arraylist的迭代遍历过程中同时进行修改,会抛出异常java.util.ConcurrentModification ...

  3. java 集合list遍历时删除元素

    本文探讨集合在遍历时删除其中元素的一些注意事项,代码如下 import java.util.ArrayList; import java.util.Iterator; import java.util ...

  4. [转】Python--遍历列表时删除元素的正确做法

    转自:https://blog.csdn.net/cckavin/article/details/83618306   一.问题描述 这是在工作中遇到的一段代码,原理大概和下面类似(判断某一个元素是否 ...

  5. 遍历list时删除元素的正确做法

    我们往往会遇到需要删除list中满足条件的元素.举例: List<string> list_str =new List<string>() { "A",&q ...

  6. Java集合遍历时删除

    public static void main(String[] args){ List<Integer> list = new ArrayList<Integer>(); l ...

  7. Java遍历时删除List、Set、Map中的元素(源码分析)

    在对List.Set.Map执行遍历删除或添加等改变集合个数的操作时,不能使用普通的while.for循环或增强for.会抛出ConcurrentModificationException异常或者没有 ...

  8. js 遍历集合删除元素

    js 遍历集合删除元素 /** * 有效的方式 - 改变下标,控制遍历 */ for (var i = 0; i < arr.length; i++) { if (...) { arr.spli ...

  9. Java HashMap 如何正确遍历并删除元素

    (一)HashMap的遍历 HashMap的遍历主要有两种方式: 第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况. HashMap<K ...

随机推荐

  1. fremarker导出word list

    import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java ...

  2. 四种常用的access连接方式

    整理出四种常用的access连接方式,当然,第1种这是最常用的(推荐使用).1. set dbconnection=Server.CreateOBJECT("ADODB.CONNECTION ...

  3. AI应用开发实战

    AI应用开发实战 出发点 目前,人工智能在语音.文字.图像的识别与解析领域带来了跨越式的发展,各种框架.算法如雨后春笋一般,互联网上随处可见与机器学习有关的学习资源,各大mooc平台.博客.公开课都推 ...

  4. 小程序组件化框架 WePY 在性能调优上做出的探究

    作者:龚澄 导语 性能调优是一个亘古不变的话题,无论是在传统H5上还是小程序中.因为实现机制不同,可能导致传统H5中的某些优化方式在小程序上并不适用.因此必须另开辟蹊径找出适合小程序的调估方式. 本文 ...

  5. 如何在JSP中获得Cookie对象

    Cookie cookies[]=request.getCookies(); //读出用户硬盘上的Cookie,并将所有的Cookie放到一个cookie对象数组里面 Cookie sCookie=n ...

  6. 高德地图JS API获取经纬度,根据经纬度获取城市

    <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...

  7. 【RL-TCPnet网络教程】第34章 RL-TCPnet之SMTP客户端

    第34章      RL-TCPnet之SMTP客户端 本章节为大家讲解RL-TCPnet的SMTP应用,学习本章节前,务必要优先学习第33章的SMTP基础知识.有了这些基础知识之后,再搞本章节会有事 ...

  8. Java面试题整理:这些Java程序员面试中经常遇见的题目,必须掌握才能有好结果

    1.是否可以从一个static方法内部发出对非static方法的调用? 不可以.因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时 ...

  9. [Swift]LeetCode304. 二维区域和检索 - 矩阵不可变 | Range Sum Query 2D - Immutable

    Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...

  10. [Swift]LeetCode657. 机器人能否返回原点 | Robot Return to Origin

    There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its mov ...