IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> ArrayList使用remove()移除元素失败 -> 正文阅读

[数据结构与算法]ArrayList使用remove()移除元素失败

有关ArrayList的源码解析以及HashMap、Set等相关内容在原来的文章中已经介绍过了,这里就不再赘述。今天这篇文章单刀直入将今天在工作中遇到的一个问题,ArrayList调用remove移除元素失败?

从源码角度来ArrayList的扩容机制与LinkedList链表的维护与查找_任天柳-CSDN博客](https://blog.csdn.net/lmlzww/article/details/117635696?spm=1001.2014.3001.5501)

Java集合——HashMap源码_任天柳-CSDN博客](https://blog.csdn.net/lmlzww/article/details/122029568?spm=1001.2014.3001.5501)

抽离业务之后的代码

public static void main(String[] args) {
    ArrayList<Integer> targetIndex = new ArrayList<>();
    targetIndex.add(0);
    LinkedList<String> dataSource = new LinkedList<>();
    dataSource.add("one");
    dataSource.add("tow");
    dataSource.add("three");
    //移除失败[one, tow, three]
    targetIndex.forEach(item->{
        dataSource.remove(item);
    });
    System.out.println(dataSource.toString());
    //移除成功[tow, three]
    targetIndex.forEach(item->{
        dataSource.remove(item.intValue());
    });
    System.out.println(dataSource.toString());
}

原理概述

简单的来说之所以第一遍遍历的时候没有遍历成功,这是由于多态机制的存在,在list中remove的实现有两个方法一个是根据索引进行移除的一个是根据对象进行移除。在第一个循环中我们传递进去的参数是Interge形式的,在调用的时候他会调用:

public boolean remove(Object o) {
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

我们可以看出他是将入参当做list中的一个值,而非是一个索引,通o.equals(x.item)来决定是要对那个元素进行移除,而我们传递进去的索引,所以在这个场景中他就没有找到要移除的元素。这里可以想到如果dataSource的类型也是Integer,那用第一种方法进行移除就更有意思了,因为他有可能是碰巧对应上的。这时候数据就会发生混乱。

而第二中的遍历方式中我们采用了intValue(),这个方法的返回值是一个int,所以remove方法就调用了:

public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

这两个方法的不同点在于第一种是通过比对集合内的值来寻找要移除的元素,而第二中则是通过索引直接找到要移除的元素。这里重点点一下第二种方法如何确定要移除的元素。我们常见的List有两种一个是底层实现为数组的ArrayList、一种为底层实现为链表的LinkList。

ArrayList移除元素的操作是通过index直接确定下标元素的位置,之后通过数组拷贝来实现数组的一个整体前移。LinkList是链表,他的index实际上是一个虚拟的,他是通过index确定要遍历多少个链表元素,来确定要移除的目标元素。但是写JDK的大佬们很机智,他们并没有直接简单的从头或是从尾遍历,而是采用一个类似于二分的机制根据index来决定这个元素从头遍历还是从尾遍历。这一点感觉是LinkList的精髓所在:

Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 15:50:37  更:2022-02-28 15:53:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 16:56:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码