Java中List遍历删除元素remove()的方法

作者:素小暖 时间:2022-07-12 12:27:10 

今天碰见根据条件进行list遍历remove的问题,第一时间就是简单for循环remove,只知道这么写不行,不安全,可是为什么呢?你想过吗?下面就关于List遍历remove的问题,深挖一下!

一、几种常见的遍历方式

1、普通for循环

Java中List遍历删除元素remove()的方法

2、高级for循环

Java中List遍历删除元素remove()的方法

3、iterator和removeIf

Java中List遍历删除元素remove()的方法

4、stream()

Java中List遍历删除元素remove()的方法

5、复制

Java中List遍历删除元素remove()的方法

6、普通for循环 --> 倒序方式

Java中List遍历删除元素remove()的方法

二、源码篇

1、普通for循环出错原因


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

/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
 modCount++;
 int numMoved = size - index - 1;
 if (numMoved > 0)
   //remove会导致之后的元素往前移动,而下标不改变时就会出现bug
   System.arraycopy(elementData, index+1, elementData, index,
            numMoved);
 elementData[--size] = null; // clear to let GC do its work
}

我们在删除某个元素后,list的大小发生了变化,这时候你的的索引也会发生变化,这时就会导致你在遍历的时候漏掉某些元素。
比如当你删除第1个元素后,我们如果还是继续根据索引访问第2个元素时,因为删除的关系,后面的元素都往前移动了一位,所以实际访问的是第3个元素。
所以这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。

2、高级for循环出错原因

foreach其实是用迭代器来进行遍历的,而在遍历时直接使用arraylist的remove方法会导致什么问题呢?

可以再看一下fastremove和迭代器遍历的内部代码:

Java中List遍历删除元素remove()的方法

Java中List遍历删除元素remove()的方法

最后导致抛出上面异常的其实就是这个,简单说,调用list.remove()方法导致modCount和expectedModCount的值不一致而报异常 


final void checkForComodification() {
 if (modCount != expectedModCount)
   throw new ConcurrentModificationException();
}
//调用next时会调用checkForComodification方法检查 这两个字段
//而fastRemove里面只对modCount 进行了改变 导致抛出异常
public E next() {
 checkForComodification();
 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];
}

所以遍历时remove并不适用于foreach。

3、java8中新方法removeIf


//内部其实就是迭代器遍历
default boolean removeIf(Predicate<? super E> filter) {
 Objects.requireNonNull(filter);
 boolean removed = false;
 final Iterator<E> each = iterator();
 while (each.hasNext()) {
   if (filter.test(each.next())) {
     each.remove();
     removed = true;
   }
 }
 return removed;
}

和迭代器差不多,内部实现也是迭代器。

三、总结

1、在不考虑内存大小会不会出现OOM的时候,采取复制一个新的list的方法速度更快,适用于集合中对象不算多的时候,毕竟只需要add操作。

2、当集合中元素过多时,复制list就显得有些笨重了,采用迭代器的方式进行遍历较快一些,并且不用关注小角标的变化。

3、不考虑性能的时候使用removeIf方法,代码简洁明了。

4、当要针对角标进行元素的remove时,使用倒序遍历的方式最为妥当。

来源:https://blog.csdn.net/guorui_java/article/details/110098348

标签:Java,List,遍历,删除元素
0
投稿

猜你喜欢

  • Android内容提供者ContentProvider用法实例分析

    2021-06-25 09:33:04
  • java中functional interface的分类和使用详解

    2021-09-15 15:59:20
  • 详解利用Spring加载Properties配置文件

    2023-04-04 20:53:13
  • Android 添加系统设置属性的实现及步骤

    2021-12-31 01:11:02
  • C#算法之回文数

    2022-06-26 20:02:38
  • 浅谈SpringCache与redis集成实现缓存解决方案

    2022-10-12 01:11:17
  • Java中Date和Calendar常用方法

    2023-11-12 07:35:32
  • Android Handler runWithScissors 梳理流程解析

    2023-01-29 11:51:27
  • C#词法分析器之词法分析的使用详解

    2022-01-26 04:22:13
  • java门禁系统面向对象程序设计

    2023-08-25 02:25:53
  • LINQ基础之Join和UNION子句

    2022-04-09 23:27:50
  • C#使用Json.Net对JSON与对象的序列化与反序列化

    2023-04-08 07:22:25
  • SpringBoot配置log4j输出日志的案例讲解

    2023-07-30 18:54:50
  • Java中为什么start方法不能重复调用而run方法可以?

    2023-11-15 03:04:02
  • SpringBoot如何动态改变日志级别

    2021-08-29 02:32:17
  • Android一行代码实现圆形头像

    2022-02-23 02:00:25
  • SpringCloud网关Gateway架构解析

    2023-03-17 00:18:42
  • C#实现无损压缩图片代码示例

    2022-01-23 19:59:25
  • Android实现摇一摇功能

    2023-07-23 20:21:11
  • c#获取数组中最大数的值

    2022-07-20 07:49:02
  • asp之家 软件编程 m.aspxhome.com