Java中ArrayList的删除元素总结

您所在的位置:网站首页 arraylist删除元素后长度会变吗 Java中ArrayList的删除元素总结

Java中ArrayList的删除元素总结

2023-08-21 05:41| 来源: 网络整理| 查看: 265

Java中循环遍历元素,一般有for循环遍历,foreach循环遍历,iterator遍历。

先定义一个List对象 List list = new ArrayList(); list.add("1"); list.add("2"); list.add("3"); 一、普通for循环遍历 for (int i = 0; i < list.size(); i++) { System.out.println(i + ":" + list.get(i)); String s = list.get(i); if ("1".equals(s)) { list.remove(s); // i--; } } System.out.println(list);

输出结果为

0:1 1:3 [2, 3]

这种删除方法明显有问题,遗漏了被删除元素后的一个元素。 这种情况下,如果被删除元素切好是List中最后一个元素,则输出结果恰好正常。 解决方法: 遗漏元素是因为删除元素后,List的size已经减1,但i不变,则i位置元素等于被跳过,不在循环中处理。 若if代码块中调用remove函数后,加上i--,则能避免这种错误。

二、Iterator遍历 Iterator iterator = list.iterator(); while (iterator.hasNext()){ String str = iterator.next(); System.out.println(str); if("2".equals(str)) { iterator.remove(); } } System.out.println(list);

输出结果为

1 2 3 [1, 3]

结论: 最安全的遍历中删除元素方法。 借用了Iterator,删除元素用的也是Iterator的remove方法,而不是List中的。

三、foreach循环遍历 for (String s : list) { System.out.println(s); if ("2".equals(s)) { list.remove(s); } }

现象: 删除元素2:正常输出

1 2 [1, 3]

删除元素1或3:报错

java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859)

foreach的本质 通过反编译,foreach的代码实现如下:

Iterator itr3 = list.iterator(); while(itr3.hasNext()) { String s = (String)itr3.next(); System.out.println(s); if ("2".equals(s)) { list.remove(s); } }

对比后发现,foreach实质上也是使用Iterator进行遍历。 不同的地方在于,一个使用Iterator的删除方法,一个使用List的删除方法。 问题出在 list.remove(s); 代码中。 我们查看一下ArrayList的报错相关代码。代码如下:

public boolean hasNext() { return cursor != size; } public E next() { checkForComodification();//859行 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]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException();//909行 } public boolean remove(Object o) { ··· fastRemove(index); ··· } private void fastRemove(int index) { modCount++; ··· }

其中size和modCount为ArrayList属性,cursor和expectedModCount为ArrayList.Itr属性。

size: List长度 modCount: List在结构上被修改的次数 cursor: Itr中下一个被返回的元素的下标 expectedModCount: 属于ArrayList.Itr,与modCount类似,初始化值等于modCount值。

输出分析

1. 报错是因为remove方法改变了modCount,导致next方法时checkForComodification检查不通过,抛出异常。 2. 移除2时正常:因为2刚好是倒数第二个元素,移除后size-1,在hasNext方法中已结束循环,不在调用next方法。虽然不报错,但会使最后一个元素被跳过,没有进入循环。 3. 移除1或3失败略有不同:remove(3)后,size减1,cursor已经比size大1,但由于hasNext方法是 cursor!=size,还是会进入循环,在next方法中才会报错。如果hasNext方法是 cursor>size ,移除3的情形会类似于移除2(不报错,直接退出进入循环)。 四、结论及其他 集合中遍历移除元素保险起见都是使用Iterator,这没什么好争议的。写这么多,只是为了看代码,探究其底层原因。 Java8中的删除方法removeIf,如下,其实也是使用Iterator。list.removeIf(e->e.equals("2")); Java8中使用如下方式删除,本质上是new了一个List,结果已经不是原List了。类似的,上述的遍历中,new一个新的List,将需要的元素add进入也是可行的。list = list.stream().filter(l->!l.equals("2")).collect(Collectors.toList());


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3