Java List
在进行 remove()
方法是通常容易踩坑,主要有以下几点:
循环时:问题在于,删除某个元素后,因为删除元素后,后面的元素都往前移动了一位,而你的索引+1,所以实际访问的元素相对于删除的元素中间间隔了一位。
错误的写法
使用 for
循环不进行额外处理
1 2 3 4 5 6 for (int i=0 ;i<list.size();i++) { if (list.get(i)%2 ==0 ) { list.remove(i); } }
使用 foreach
循环
1 2 3 4 5 6 for (Integer i:list) { if (i%2 ==0 ) { list.remove(i); } }
抛出异常:java.util.ConcurrentModificationException
;foreach
的本质是使用迭代器实现,每次进入 for (Integer i:list)
时,会调用 ListItr.next()
方法;
继而调用 checkForComodification()
方法, checkForComodification()
方法对操作集合的次数进行了判断,如果当前对集合的操作次数与生成迭代器时不同,抛出异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public E next () { checkForComodification(); if (!hasNext()) { throw new NoSuchElementException(); } lastReturned = next; next = next.next; nextIndex++; return lastReturned.item; } final void checkForComodification () { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
正确的写法
使用 for
循环,并且同时改变索引
1 2 3 4 5 6 7 for (int i=0 ;i<list.size();i++) { if (list.get(i)%2 ==0 ) { list.remove(i); i--; } }
使用 for
循环,倒序进行
1 2 3 4 5 6 for (int i=list.size()-1 ;i>=0 ;i--) { if (list.get(i)%2 ==0 ) { list.remove(i); } }
使用 while
循环,删除了元素,索引便不+1,在没删除元素时索引+1
1 2 3 4 5 6 7 8 9 int i=0 ;while (i<list.size()) { if (list.get(i)%2 ==0 ) { list.remove(i); }else { i++; } }
使用迭代器方法(推荐)
只能使用迭代器的 remove()
方法,使用列表的 remove()
方法是错误的
1 2 3 4 5 6 Iterator<Integer> itr = list.iterator(); while (itr.hasNext()) { if (itr.next()%2 ==0 ) itr.remove(); }
优化
下面来谈谈当数据量过大时候,需要删除的元素较多时,如何用迭代器进行性能的优化,对于 ArrayList
这几乎是致命的,从一个 ArrayList
中任意的地方删除元素操作都是昂贵的时间复杂度为 O(n²),那么接下来看看 LinkeedList
是否可行。LinkedList
暴露了两个问题,一个:是每次的 Get
请求效率不高,而且,对于 remove
的调用同样低效,因为达到位置 I 的代价是昂贵的。
使用迭代器的方法删除元素,对于 LinkedList
,对该迭代器的 remove()
方法的调用只花费常数时间,因为在循环时该迭代器位于需要被删除的节点,因此是常数操作。对于一个 ArrayList
,即使该迭代器位于需要被删除的节点,其 remove()
方法依然是昂贵的,因为数组项必须移动。
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public static void main (String[] args) { List<Integer> arrList1 = new ArrayList<>(); for (int i = 0 ; i < 100000 ; i++) { arrList1.add(i); } List<Integer> linList1 = new LinkedList<>(); for (int i = 0 ; i < 100000 ; i++) { linList1.add(i); } List<Integer> arrList2 = new ArrayList<>(); for (int i = 0 ; i < 100000 ; i++) { arrList2.add(i); } List<Integer> linList2 = new LinkedList<>(); for (int i = 0 ; i < 100000 ; i++) { linList2.add(i); } removeEvens(arrList1, "ArrayList" ); removeEvens(linList1, "LinkedList" ); removeEvensByIterator(arrList2, "ArrayList" ); removeEvensByIterator(linList2, "LinkedList" ); } public static void removeEvensByIterator (List<Integer> lst, String name) { long sTime = new Date().getTime(); Iterator<Integer> itr = lst.iterator(); while (itr.hasNext()) { if (itr.next() % 2 == 0 ) itr.remove(); } System.out.println(name + "使用迭代器时间:" + (new Date().getTime() - sTime) + "毫秒" ); } public static void removeEvens (List<Integer> list, String name) { long sTime = new Date().getTime(); int i = 0 ; while (i < list.size()) { if (list.get(i) % 2 == 0 ) { list.remove(i); } else { i++; } } System.out.println(name + "不使用迭代器的时间" + (new Date().getTime() - sTime) + "毫秒" ); }
运行结果:
1 2 3 4 ArrayList 不使用迭代器的时间242 毫秒LinkedList 不使用迭代器的时间5613 毫秒ArrayList 使用迭代器时间:224 毫秒LinkedList 使用迭代器时间:7 毫秒
If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !