List的remove方法误区

1、在遍历List的时候,不要使用remove方法,会报错


Blog解释如下:


在Java中有时候我们会需要对List里面的符合某种业务的数据进行删除,但是如果不了解里面的机制就容易掉入“陷阱”导致遗漏或者程序异常。本文以代码例子的方式进行说明该问题。

1、采用索引下标遍历的方式

我们看这段示例代码:

   
   
List < String > list = new ArrayList < String >();
list . add ( "aa" );
list . add ( "bb" );
list . add ( "bb" );
list . add ( "cc" );
list . add ( "dd" );
for ( int i = 0 ; i < list . size (); i ++) {
if ( "b" == list . get ( i )) {
list . remove ( i );
}
System . out . println ( list . get ( i ));
}
System . out . println ( "The Result Is : " + list . toString ());


该代码运行结果如下:

    
    
aa
bb
cc
dd
The Result Is : [ aa , bb , cc , dd ]

我们是想删除等于"bb"的元素,但结果显示只删除了一个"bb",另一个"bb"被遗漏了,原因是:删除了第一个2后,集合里的元素个数减1,后面的元素往前移了1位,导致了第二个2被遗漏了。

2、采用For循环遍历的方式

   
   
List<String> list = new ArrayList<String>();
list.add("aa");
list.add("bb");
list.add("bb");
list.add("cc");
list.add("dd");
 
// for (int i = 0; i < list.size(); i++) {
//
// if ("bb" == list.get(i)) {
// list.remove(i);
// }
// System.out.println(list.get(i));
// }
for (String value : list) {
if ("bb".equalsIgnoreCase(value)) {
list.remove(value);
}
System.out.println(value);
}
System.out.println("The Result Is : " + list.toString());



程序运行结果:

   
   
aa
bb
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at commonTest.RemoveTest.main(RemoveTest.java:25)

从运行结果看到程序抛ConcurrentModificationException。

参考了一下烽火哥的解释http://www.cnblogs.com/frankliiu-java/articles/1759460.html

Java中的For each实际上使用的是iterator进行处理的。而iterator是不允许集合在iterator使用期间删除的。所以导致了iterator抛出了ConcurrentModificationException 。


后来看了下JDK的API中对该异常描述,如下:

   
   
public class ConcurrentModificationException extends RuntimeException当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
例如,某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。通常在这些情况下,迭代的结果是不确定的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败(fail-fast)迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。
注意,此异常不会始终指出对象已经由不同线程并发修改。如果单线程发出违反对象协定的方法调用序列,则该对象可能抛出此异常。例如,如果线程使用快速失败迭代器在 collection 上迭代时直接修改该 collection,则迭代器将抛出此异常。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败操作会尽最大努力抛出 ConcurrentModificationException。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。

3、在遍历List过程中删除元素的正确做法

   
   
List<String> list = new ArrayList<String>();
list.add("aa");
list.add("bb");
list.add("bb");
list.add("cc");
list.add("dd");
 
// for (int i = 0; i < list.size(); i++) {
//
// if ("bb" == list.get(i)) {
// list.remove(i);
// }
// System.out.println(list.get(i));
// }
// for (String value : list) {
//
// if ("bb".equalsIgnoreCase(value)) {
//
// list.remove(value);
// }
// System.out.println(value);
// }
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String value = (String) iterator.next();
System.out.println(value);
if ("bb".equalsIgnoreCase(value)) {
iterator.remove();
}
}
System.out.println("The Result Is : " + list.toString());

输出结果:

   
   
aa
bb
bb
cc
dd
The Result Is : [aa, cc, dd]

我们看到两个"bb"全部被删除了,最后结果剩下"aa","cc","dd"完全正确。

但对于iterator的remove()方法,也有需要我们注意的地方:

1、每调用一次iterator.next()方法,只能调用一次remove()方法。

2、调用remove()方法前,必须调用过一次next()方法。

以下是JDK-API中对于remove()方法的描述:

   
   
void remove()
从迭代器指向的集合中移除迭代器返回的最后一个元素(可选操作)。每次调用 next 只能调用一次此方法。如果进行迭代时用调用此方法之外的其他方式修改了该迭代器所指向的集合,则迭代器的行为是不明确的。
抛出:UnsupportedOperationException - 如果迭代器不支持 remove 操作。IllegalStateException - 如果尚未调用 next 方法,或者在上一次调用 next 方法之后已经调用了remove 方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值