首页 > 其他分享 >遍历List移除元素时踩坑点

遍历List移除元素时踩坑点

时间:2023-11-20 13:33:21浏览次数:25  
标签:iterator 坑点 元素 List numbers 移除 ListIterator itemsToRemove

1、问题描述

在遍历List并在循环体中移除元素时需要注意以下几点

  1. 移除元素后数据总量会越来越小,可能造成数组下标越界

  2. 移除元素后,每个元素原有位置也会发生改变,需确认移除的元素是否是真正需要移除的

  3. 由于删除元素后,每个元素位置前移,会有部分数据直接跳过循环

    例如 数组中有以下数据

    i 0 1 2 3 4
    val 1 2 3 4 5

    当 i = 1 时 移除元素2,3会前移,下标1的值变为3,下标2的值变为4,后面的元素依次前移

    当 i = 1 的循环体结束后,i 自增,进入 i = 2 的循环体,此时 i = 2 对应的值为4 ,3被跳过,不经过循环体

2、问题复现

假设我有一个数组,需要移除下标为 0,1,2,3 的元素

image-20231120125313864

代码如下:

 @Test
    public void testRemove1(){
        List<Integer> numbers = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            numbers.add(i);
        }
        System.out.println("old_numbers:"+numbers);

        List<Integer> itemsToRemove = new ArrayList<>();
        itemsToRemove.add(0);
        itemsToRemove.add(1);
        itemsToRemove.add(2);
        itemsToRemove.add(3);
        System.out.println(itemsToRemove);

        //移除元素时数据总长度减小,元素原本位置改变
        // 不仅会造成数组下标越界,移除的元素也并非想要移除的元素
        for (int  integer : itemsToRemove) {
            numbers.remove(integer);
        }

        System.out.println("new_numbers:"+numbers);
    }

假设我只需要移除下标为 0,1 的元素,下标0 对应的值应为0,下标1 对应的值应为1

移除后的元素应该是 [2,3,4] 但结果却是 [1,3,4]

image-20231120125730760

3、解决方案

可以使用 ListIterator 来安全地在迭代过程中删除元素,而不影响后续元素的位置

ListIterator 是 Java 中 List 接口提供的一个迭代器,相比普通的迭代器,它具有双向遍历的能力,允许在迭代过程中插入、删除元素,以及获取当前位置的索引。这使得在使用 List 类型的集合时,特别是在需要在迭代过程中修改集合的情况下,ListIterator 是一个更加强大的工具。

image-20231120130455982

    @Test
    public void testRemove2(){
        List<Integer> numbers = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            numbers.add(i);
        }
        System.out.println("old_numbers:"+numbers);

        List<Integer> itemsToRemove = new ArrayList<>();
        itemsToRemove.add(0);
        itemsToRemove.add(1);
        itemsToRemove.add(2);
        itemsToRemove.add(3);
        System.out.println(itemsToRemove);
        

        // 使用 ListIterator 正序遍历并删除需要删除的元素
        ListIterator<Integer> iterator = numbers.listIterator();
        while (iterator.hasNext()) {
            int indexToRemove = iterator.nextIndex();
            Integer nextVal = iterator.next();// 移动到下一个位置

//            System.out.println("index:"+indexToRemove+",value:"+nextVal);

            if (itemsToRemove.contains(nextVal)){
                iterator.remove(); // 安全地移除当前元素
                System.out.println("移除元素 index:"+indexToRemove+",value:"+nextVal);
            }
        }


        // 使用 ListIterator 逆序遍历并删除需要删除的元素
//        ListIterator<Integer> iterator = numbers.listIterator(numbers.size());
//        while (iterator.hasPrevious()) {
//            int indexToRemove = iterator.previousIndex();
//            Integer previousVal = iterator.previous();// 移动到上一个位置
//
//            if (itemsToRemove.contains(previousVal)){
//                iterator.remove(); // 安全地移除当前元素
//                System.out.println("移除元素 index:"+indexToRemove+",value:"+previousVal);
//            }
//        }
        
        System.out.println("new_numbers:"+numbers);
    }

ListIterator常见方法

  • hasNext():判断是否还有下一个元素。
  • hasPrevious():判断是否还有上一个元素。
  • next():返回下一个元素。
  • previous():返回上一个元素。
  • nextIndex():返回下一个元素的索引。
  • previousIndex():返回上一个元素的索引。
  • add(E element):在当前位置插入指定的元素。
  • set(E element):用指定的元素替换上一次调用 next()previous() 返回的元素。
  • remove():移除上一次调用 next()previous() 返回的元素。

ListIterator 两种初始化方式

ListIterator 有两个不同的初始化方式,分别是:

  1. ListIterator<Integer> iterator = numbers.listIterator():通过这种方式初始化的 ListIterator 位于列表的开始位置,即索引为 0 的位置。当你使用 iterator.next() 时,它会返回列表的第一个元素。
  2. ListIterator<Integer> iterator = numbers.listIterator(numbers.size()):通过这种方式初始化的 ListIterator 位于列表的末尾位置,即索引为 list.size() 的位置。当你使用 iterator.previous() 时,它会返回列表的最后一个元素。

总的来说,这两种方式初始化的 ListIterator 分别定位在列表的开始和末尾位置,选择取决于你是希望从前向后遍历还是从后向前遍历。

示例:

javaCopy codeList<String> myList = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));

// 从前向后遍历
ListIterator<String> iteratorForward = myList.listIterator();
while (iteratorForward.hasNext()) {
    System.out.print(iteratorForward.next() + " ");
}
// 输出: A B C D

System.out.println();

// 从后向前遍历
ListIterator<String> iteratorBackward = myList.listIterator(myList.size());
while (iteratorBackward.hasPrevious()) {
    System.out.print(iteratorBackward.previous() + " ");
}
// 输出: D C B A

在这个示例中,iteratorForward 从前向后遍历列表,而 iteratorBackward 从后向前遍历列表。

标签:iterator,坑点,元素,List,numbers,移除,ListIterator,itemsToRemove
From: https://www.cnblogs.com/an-shiguang/p/17843744.html

相关文章

  • Learning to rank: from pairwise approach to listwise approach
    目录概ListNetPermutationProbabilityTop-kProbabilityCaoZ.,QinT.,LiuT.,TsaiM.andLiH.Learningtorank:frompairwiseapproachtolistwiseapproach.ICML,2008.概ListwiseRanking.ListNet以文档检索为例,假设我们有query\(q\)和一堆候选的文档......
  • Qt项目中CMakeLists.txt文件
    cmake_minimum_required(VERSION3.5)project(testNameLANGUAGESCXX)set(CMAKE_AUTOUICON)set(CMAKE_AUTOMOCON)set(CMAKE_AUTORCCON)set(CMAKE_CXX_STANDARD17)set(CMAKE_CXX_STANDARD_REQUIREDON)find_package(Qt5COMPONENTSREQUIREDWidgetsSqlCharts......
  • Qt 基本CMakeLists.txt 文件
    cmake_minimum_required(VERSION3.5)project(untitledVERSION0.1LANGUAGESCXX)set(CMAKE_AUTOUICON)set(CMAKE_AUTOMOCON)set(CMAKE_AUTORCCON)set(CMAKE_CXX_STANDARD17)set(CMAKE_CXX_STANDARD_REQUIREDON)find_package(QTNAMESQt6Qt5REQUIREDCOM......
  • list对象转数组
    list对象转数组packagecom.example.core.mydemo.json5;importorg.apache.commons.collections4.CollectionUtils;importjava.util.ArrayList;importjava.util.List;/***list对象转数组*/publicclassArrayTest{publicstaticvoidmain(String[]args)......
  • 数组类算法题——数组中移除指定元素
    数组中移除指定元素 题目:给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。解......
  • 《Java编程思想第四版》学习笔记37--关于 TextField的ActionListener接收器
    //:TextNew.java//TextfieldswithJava1.1eventsimportjava.awt.*;importjava.awt.event.*;importjava.applet.*;publicclassTextNewextendsApplet{Buttonb1=newButton("GetText"),b2=newButton("SetText");TextFie......
  • 更新数据状态多大一批比较好另外list里面放可以多少数据由什么决定
    更新数据的批量大小没有一个固定的标准,它取决于多个因素,包括数据库的性能、网络环境、服务器资源以及具体的业务需求。一般来说,使用适当的批量大小可以提高更新操作的效率。如果批量大小过小,会增加数据库连接和网络通信的开销;而如果批量大小过大,可能会占用过多的内存和数据库资源,导......
  • java对象类型强转 java将object强转为list对象
    List类型的Object数据需要遍历操作时,需要将Object类型转换为List类型,转换方式如下。/***object转list*@paramobj需要转换的List对象*@paramclazzList中元素的class*@param<T>*@return*/publicstatic<T>List<T>......
  • 27. 移除元素
    2023-11-1627.移除元素-力扣(LeetCode)思路:双指针classSolution{publicintremoveElement(int[]nums,intval){//双指针intj=0;for(inti=0;i<nums.length;i++){if(nums[i]!=val){nums[j]=nums[i];......
  • 无涯教程-Dart - Using the List.replaceRange() 函数
    dart:core库中的List类提供了replaceRange()函数来修改List元素,此函数替换指定范围内的元素的值。使用List.replaceRange()函数的语法如下所示-List.replaceRange(intstart_index,intend_index,Iterable<items>)Start_index   -代表要开始替换的索引位置的整数。......