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

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

一、几种常见的遍历方式

1、普通for循环

2、高级for循环

3、iterator和removeIf

4、stream()

5、复制

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

二、源码篇

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和迭代器遍历的内部代码:

最后导致抛出上面异常的其实就是这个,简单说,调用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时,使用倒序遍历的方式最为妥当。

到此这篇关于Java中List遍历删除元素remove()的方法的文章就介绍到这了,更多相关Java List遍历删除元素内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-11-25

Java如何在List或Map遍历过程中删除元素

遍历删除List或Map中的元素有很多种方法,当运用不当的时候就会产生问题.下面通过这篇文章来再学习学习吧. 一.List遍历过程中删除元素 使用索引下标遍历的方式 示例:删除列表中的2 public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(2); list.add(3); list.add(

Java使用entrySet方法获取Map集合中的元素

本文为大家分享了使用entrySet方法获取Map集合中元素的具体代码,供大家参考,具体内容如下 /*--------------------------------- 使用entrySet方法取出Map集合中的元素: ....该方法是将Map集合中key与value的关系存入到了Set集合中,这个关系的数据类型是Map.Entry ....entrySet方法返回值类型的具体写法为:Set< Map.Entry<KeyType , ValueType> > -----------

Java使用keySet方法获取Map集合中的元素

本文为大家分享了Map集合中利用keySet方法获取所有的元素值,供大家参考,具体内容如下 /*--------------------------- Map集合中利用keySet方法获取所有的元素值: ....keySet方法:将Map中的所有key值存入到Set集合中, ....利用Set集合提供的迭代器获取到每一个key值,再通过key值获得相应的value值 ----------------------------*/ package pack03; import java.util.*

java 键盘输入一个数,输出数组中指定元素的示例

如下所示: package com.lcn.day05; import java.util.Scanner; public class ArrayDemo7 { /** *键盘输入一个数,输出数组中指定元素 */ public static void main(String[] args) { // 定义一个数组 int[] array = new int[]{123,456,789,321,654,987}; //创建输入对象 Scanner sc = new Scanner(System.i

Java封装数组实现包含、搜索和删除元素操作详解

本文实例讲述了Java封装数组实现包含.搜索和删除元素操作.分享给大家供大家参考,具体如下: 前言:在上一小节中我们已经会了如何获取和如何修改数组中的元素,在本小节中我们将继续学习如何判断某个元素是否在数组中存在.查询出某个元素在数组中的位置.以及删除数组中元素等方法的编写. 1.查找数组中是否包含元素e,返回true或false //查找数组中是否包含元素e public boolean contains(int e) { for (int i = 0; i < size; i++) { if

python实现从字典中删除元素的方法

本文实例讲述了python实现从字典中删除元素的方法.分享给大家供大家参考.具体分析如下: python的字典可以通过del方法进行元素删除,下面的代码详细演示了这一过程 # Create an empty dictionary d = {} # Add an item d["name"] = "Fido" assert d.has_key("name") # Delete the item del d["name"] ass

PHP从数组中删除元素的四种方法实例

茴香豆的"茴"字有四种写法,PHP从数组中删除元素也有四种方法 ^_^. 删除一个元素,且保持原有索引不变 使用 unset 函数,示例如下: <?php $array = array(0 => "a", 1 => "b", 2 => "c"); unset($array[1]); //↑ 你想删除的key ?> 输出: Array (     [0] => a     [2] =>

php数组中删除元素之重新索引的方法

如果要在某个数组中删除一个元素,可以直接用的unset,但今天看到的东西却让我大吃一惊 复制代码 代码如下: <?php $arr = array('a','b','c','d'); unset($arr[1]); print_r($arr); ?> print_r($arr)之后,结果却不是那样的,最终结果是 Array ( [0] => a [2] => c [3] => d ) 那么怎么才能做到缺少的元素会被填补并且数组会被重新索引呢?答案是 array_splice(

Java函数式编程(四):在集合中查找元素

查找元素 现在我们对这个设计优雅的转化集合的方法已经不陌生了,但它对查找元素却也是无能为力.不过filter方法却是为这个而生的. 我们现在要从一个名字列表中,取出那些以N开头的名字.当然可能一个也没有,结果可能是个空集合.我们先用老方法实现一把. 复制代码 代码如下: final List<String> startsWithN = new ArrayList<String>(); for(String name : friends) { if(name.startsWith(&

php数组中删除元素的实现代码

复制代码 代码如下: <?php $arr = array('a','b','c','d'); unset($arr[1]); print_r($arr); ?> print_r($arr)之后,结果却不是那样的,最终结果是 Array ( [0] => a [2] => c [3] => d 那么怎么才能做到缺少的元素会被填补并且数组会被重新索引呢?答案是array_splice(): 复制代码 代码如下: <?php $arr = array('a','b','c'