List集合中删除对象浅谈 这个问题看似好像挺简单挺幼稚的,然而就这个幼稚的问题还曾经困扰过我一阵呢,其实犯这个错误的根本原因是我们对于基础知识的理解和掌握上还有不足的表现,基本功有些人总是觉得不重要,认为更多的扩大学习的广度才是最重要的,我认为这是很容易犯的,同时也是很致命的错误观点! 对基础知识掌握不牢靠,或理解不深刻,写出的代码必然会有40%的概率是错误的、低效的,一段代码不是说没有编译错误就一定是对的,不是说简单的运行下就一定是对的,优秀的代码,是经得起“刀山火海”般的考验的(“多线程、效率、安全”这三坐大山)。 所以重要性,我就不再强调了,相信说完后,地球人都能懂我的意思,那么下面就来看看这个有趣的现象吧。 先看看如下代码 import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class TestList { void init(List<Integer> list) { list.clear(); for (int i = 0; i < 10; i++) { list.add(i + 1); } } void remove(List<Integer> list) { for (int i = 0; i < 5; i++) { list.remove(i); } } void removeTwo(List<Integer> list) { for (int i : list) { if (i < 6) { list.remove(i); } } } void removeThree(List<Integer> list) { for (Iterator<Integer> iter = list.iterator(); iter.hasNext();) { int i = iter.next(); if (i < 6) { iter.remove(); } } } public static void main(String[] args) { TestList testList = new TestList(); List<Integer> list = new ArrayList<Integer>(); // 第一种方法 testList.init(list); testList.remove(list); System.out.println(list); // 第二种方法 try { testList.init(list); testList.removeTwo(list); System.out.println(list); } catch (Exception e) { e.printStackTrace(); } // 第三种方法 testList.init(list); testList.removeThree(list); System.out.println(list); } } 运行的结果如下: [2, 4, 6, 8, 10] java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at com.TestList.removeTwo(TestList.java:23) at com.TestList.main(TestList.java:60) [6, 7, 8, 9, 10] 上面代码逻辑很简单,就是将List集合里的前5个对象删除掉,然而结果却是相当不一样:第一个是错误的结果,第二个直接报异常,只有第三个是我们想要的结果! 那么这是为什么呢?其实主要原因来自于List中的remove()方法。我们来分析一下: 第一种方法: 第一次执行完remove方法后,并不像我们简单想象的那样就把第一个删除了,“1”这个对象被删除了没错,但是当被删除后List中“2”以后的9个对象的index索引也变了,都比原来的值减一,换句话说就是剩下的9个对象的index值为从0到8,而不是原来的从1到9了,那么第二次执行remove方法时,此时list.remove(1)删除的就是“3”这个对象(“3”的index值为1),而不是我们想象的删除“2”对象。 第二种方法: 原因跟上面一样,导致List的next()方法内部出现modCount和expectedModCount不一致导致抛出异常。 所以我们这里建议大家采用第三种方法来删除List集合中某一个对象,这样做是最简单且容易记忆的。那第一种方法和第二种方法有没有解决的办法呢?目前我知道第一种方法的解决办法,第二种应该是无解的(因为语法结构导致)。 第一种方法的改进措施: void remove(List<Integer> list) { int num = list.size() - 5; for (int i = 0; i < num; i++) { list.remove(i); i--; num--; } }
|