Java中ArrayList和LinkedList的比较

news2024/11/13 14:43:43

 

注:Joshua Bloch 就是 LinkedList 的作者

在Java中,ArrayList和LinkedList都是常用的列表实现类,它们都实现了List接口,但在内部工作原理和性能方面有显著差异。

  • ArrayList:基于动态数组实现。随着元素的增加,数组的大小会动态调整。如果数组容量不足时,会分配一个新的更大的数组并复制元素。
  • LinkedList:基于双向链表实现。每个元素是一个节点,包含指向前后元素的引用。

访问速度

  • ArrayList:支持通过索引进行随机访问(get(int index)),由于是基于数组,所以访问速度很快,时间复杂度为O(1)。
  • LinkedList:随机访问时,需要从头部或尾部遍历链表,时间复杂度为O(n),因此访问速度较慢。

内存消耗

  • ArrayList:由于是数组实现,元素仅存储数据,因此内存利用率较高,但在动态扩容时可能会浪费一些内存。
  • LinkedList:每个节点不仅存储数据,还存储两个引用(前一个和后一个节点),因此需要更多的内存。

关于 ArrayList 和 LinkedList 在添加 (add) 和删除 (remove) 操作上的性能比较,传统观点认为由于 ArrayList 需要移动数据,而 LinkedList 只需调整链表指针,因此 LinkedList 更快。然而,实际性能并不是这么简单,两者在不同情况下的表现差异较大,

ArrayList中的随机访问、添加和删除部分源码如下:

// 获取 index 位置的元素值
public E get(int index) {
    rangeCheck(index); // 首先判断 index 的范围是否合法

    return elementData(index); // 返回 elementData 数组中 index 位置的元素
}

// 将 index 位置的值设为 element,并返回原来的值
public E set(int index, E element) {
    rangeCheck(index); // 检查索引的有效性

    E oldValue = elementData(index); // 保存旧值
    elementData[index] = element; // 将新值 element 赋给 elementData 数组的 index 位置
    return oldValue; // 返回旧值
}

// 将 element 添加到 ArrayList 的指定位置
public void add(int index, E element) {
    rangeCheckForAdd(index); // 检查索引是否适合添加操作

    ensureCapacityInternal(size + 1);  // 确保内部容量足够存储新增的元素,并增加 modCount
    // 将 index 及其后的数据复制到 index+1 的位置,即从 index 开始向后挪了一位
    System.arraycopy(elementData, index, elementData, index + 1, size - index); 
    elementData[index] = element; // 在 index 处插入 element
    size++; // 增加列表大小
}

// 删除 ArrayList 指定位置的元素
public E remove(int index) {
    rangeCheck(index); // 检查索引的有效性

    modCount++; // 增加 modCount
    E oldValue = elementData(index); // 记录被移除元素的旧值

    int numMoved = size - index - 1;
    if (numMoved > 0) {
        // 向左挪一位,index 位置原来的数据已经被覆盖
        System.arraycopy(elementData, index + 1, elementData, index, numMoved);
    }
    // 清空最后一个元素
    elementData[--size] = null; 

    return oldValue; // 返回被移除的旧值
}

  LinkedList中的随机访问、添加和删除部分源码如下:

// 获得第 index 个节点的值
public E get(int index) {
    checkElementIndex(index); // 检查索引的有效性
    return node(index).item; // 返回第 index 个节点的值
}

// 设置第 index 个元素的值
public E set(int index, E element) {
    checkElementIndex(index); // 检查索引的有效性
    Node<E> x = node(index); // 定位到第 index 个节点
    E oldVal = x.item; // 保存旧值
    x.item = element; // 设置新值
    return oldVal; // 返回旧值
}

// 在 index 个节点之前添加新的节点
public void add(int index, E element) {
    checkPositionIndex(index); // 检查索引的有效性
    
    if (index == size) {
        linkLast(element); // 如果在末尾添加,直接调用 linkLast 方法
    } else {
        linkBefore(element, node(index)); // 否则,在指定位置之前添加新节点
    }
}

// 删除第 index 个节点
public E remove(int index) {
    checkElementIndex(index); // 检查索引的有效性
    return unlink(node(index)); // 删除指定节点并返回旧值
}

// 定位 index 处的节点
Node<E> node(int index) {
    // assert isElementIndex(index); // 断言索引的有效性
    // 当 index < size / 2 时,从头开始查找
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++) {
            x = x.next;
        }
        return x;
    } else { // 当 index >= size / 2 时,从尾部开始查找
        Node<E> x = last;
        for (int i = size - 1; i > index; i--) {
            x = x.prev;
        }
        return x;
    }
}
  • 随机访问(get 和 set):ArrayList 远远优于 LinkedList。ArrayList 能在 O(1) 的时间复杂度内完成,而 LinkedList 的查找时间复杂度是 O(n),性能远不如 ArrayList。
  • 插入和删除:这两种操作的性能没有简单的结论。对于 ArrayList,如果插入或删除发生在数组的末尾,操作效率非常高(O(1));但是如果操作发生在数组的中间或开头,则需要移动大量元素(O(n))。LinkedList 的插入和删除操作在理论上更快(O(1)),但由于需要遍历链表查找目标位置,因此查找阶段(O(n))会拖慢整体性能。对于插入或删除操作,LinkedList 在极端情况下(如频繁在中间插入或删除)可能优于 ArrayList,但通常两者的效率没有明显的优劣。

下面通过程序来测试一下两者插入的速度:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * 列表插入测试类
 * 本类用于测试在不同类型的列表(ArrayList和LinkedList)中插入元素的性能
 */
public class ListInsertionTest {

    /**
     * 插入测试方法,在指定索引位置插入元素
     * 
     * @param list 待测试的列表
     * @param insertions 插入的元素数量
     * @param index 插入的索引位置
     * @param listType 列表类型,用于输出信息
     */
    private static void testInsertion(List<Integer> list, int insertions, int index, String listType) {
        long startTime = System.nanoTime();

        // 在指定索引位置插入元素
        for (int i = 0; i < insertions; i++) {
            list.add(index, i);
        }

        long endTime = System.nanoTime();
        long duration = (endTime - startTime) / 1_000_000; // 转换为毫秒
        System.out.println(listType + " 在 index=" + index + " 插入 " + insertions + " 个元素,耗时: " + duration + " 毫秒");
    }

    public static void main(String[] args) {
        int initialSize = 10_000; // 初始容量
        int insertions = 10_000;  // 插入的元素数量

        // 初始化 ArrayList 和 LinkedList
        List<Integer> arrayList = new ArrayList<>(initialSize);
        List<Integer> linkedList = new LinkedList<>();

        // 初始化数据,使得列表的初始容量为 initialSize
        for (int i = 0; i < initialSize; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }

        // 测试 ArrayList
        System.out.println("ArrayList:");
        testInsertion(arrayList, insertions, 1000, "ArrayList");  // 在 index=1000 处插入
        testInsertion(arrayList, insertions, 5000, "ArrayList");  // 在 index=5000 处插入
        testInsertion(arrayList, insertions, 9000, "ArrayList");  // 在 index=9000 处插入

        // 测试 LinkedList
        System.out.println("\nLinkedList:");
        testInsertion(linkedList, insertions, 1000, "LinkedList");  // 在 index=1000 处插入
        testInsertion(linkedList, insertions, 5000, "LinkedList");  // 在 index=5000 处插入
        testInsertion(linkedList, insertions, 9000, "LinkedList");  // 在 index=9000 处插入
    }
}
ArrayList:
ArrayList 在 index=1000 插入 10000 个元素,耗时: 17 毫秒
ArrayList 在 index=5000 插入 10000 个元素,耗时: 21 毫秒
ArrayList 在 index=9000 插入 10000 个元素,耗时: 29 毫秒

LinkedList:
LinkedList 在 index=1000 插入 10000 个元素,耗时: 21 毫秒
LinkedList 在 index=5000 插入 10000 个元素,耗时: 77 毫秒
LinkedList 在 index=9000 插入 10000 个元素,耗时: 148 毫秒

从测试结果来看,ArrayList 在不同插入位置的性能表现明显优于 LinkedList。

  • ArrayList 在进行插入操作时,即使是插入到中间和靠后的位置,仍然表现出较为稳定和较快的速度。
  • LinkedList 的插入操作在前半部分表现较好,但随着插入位置的增加,性能迅速下降,不适合在长列表中频繁插入大量数据。

根据测试结果,ArrayList 更适合在大多数情况下的插入操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2150481.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小新-13 2019 Intel款IML版【81UQ】原装出厂Win10系统镜像下载

恢复lenovo联想开箱状态&#xff0c;自带预装OEM系统安装包 链接&#xff1a;https://pan.baidu.com/s/1wwPriBoIwNOAfL-YcX1F7g?pwdg7ki 提取码&#xff1a;g7ki 联想原装出厂系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软…

如何联系真正的开发者而非公司??

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

T9-猫狗识别2(暂时版qaq)

T9周&#xff1a;猫狗识别2 **一、前期工作**1.设置GPU,导入库2.导入数据3.查看数据 **二、数据预处理**1.加载数据2.可视化数据3.配置数据集 **三、构建CNN网络模型****四、编译模型****五、训练模型****六、模型评估****七、预测**八、总结&#xff08;暂时&#xff09; &…

信奥初赛解析:1-3-计算机软件系统

知识要点 软件系统是计算机的灵魂。没有安装软件的计算机称为“裸机”&#xff0c;无法完成任何工作硬件为软件提供运行平台。软件和硬件相互关联,两者之间可以相互转化&#xff0c;互为补充 计算机软件系统按其功能可分为系统软件和应用软件两大类 一、系统软件 系统软件是指…

【Redis入门到精通三】Redis核心数据类型(List,Set)详解

目录 Redis数据类型 ​编辑 1.List类型 &#xff08;1&#xff09;常见命令 &#xff08;2&#xff09;内部编码 2.Set类型 &#xff08;1&#xff09;常见命令 &#xff08;2&#xff09;内部编码 Redis数据类型 查阅Redis官方文档可知&#xff0c;Redis提供给用户的核…

【2024/09/20更新】植物大战僵尸杂交版V2.5下载

植物大战僵尸杂交版V2.5 2.5版本更新公告&#xff1a; 增加新关卡– 益智模式–两面夹击模式关卡 挑战模式关卡 增加新铲子–骷髅铲子 --银币购买-挖掉植物触发亡语或召唤骷髅僵尸 增加新植物– 4张白卡植物-通过两面夹击获得 2张金卡植物和4张星卡植物 游戏调整– 调整…

Coggle数据科学 | 科大讯飞AI大赛:玉米雄穗识别挑战赛

本文来源公众号“Coggle数据科学”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;科大讯飞AI大赛&#xff1a;玉米雄穗识别挑战赛 赛题名称&#xff1a;玉米雄穗识别挑战赛 赛题类型&#xff1a;计算机视觉、物体检测 赛题任务&…

图的应用(关键路径)

基于你设计的带权有向无环图&#xff0c;写出所有合法的关键路径&#xff0c;并算出关键路径总长度 文字描述&#xff1a;关键路径总长度的现实意义是什么&#xff1f; 1.关键路径 总长度454316 2.现实意义 从源点到汇点的所有路径中&#xff0c;具有最大路径长度的路径称…

MySQL高阶1892-页面推荐2

目录 题目 准备数据 分析数据 总结 题目 您正在为一个社交媒体网站实施一个页面推荐系统。如果页面被user_id的 至少一个朋友喜欢 &#xff0c;而 不被user_id喜欢 &#xff0c;你的系统将 推荐 一个页面到user_id。 编写一个解决方案来查找针对每个用户的所有可能的 页面…

感知笔记3:平面和物体检测

识别平面表面&#xff1a;这项技能使机器人能够检测物体通常所在的位置&#xff0c;如桌子和架子。这是搜索物体的第一步。识别物体&#xff1a;一旦您知道在哪里寻找&#xff0c;就必须在场景中识别不同的物体&#xff0c;并根据机器人的位置&#xff08;坐标系&#xff09;定…

【STL】pair 与 map:基础、操作与应用

C 标准库中提供了许多用于处理数据结构的容器和工具。pair 和 map 是两个非常有用的工具&#xff0c;广泛应用于存储和处理关联数据。在本文中&#xff0c;我们将详细介绍 pair 与 map 的相关操作&#xff0c;并结合代码实例为读者提供清晰的理解。 pair&#xff1a;成对数据的…

基于SpringBoot+Vue的家政预约平台系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

powerbi-L8-导入数据时候的动态列

背景&#xff1a; 在数据导入之后刷新的过程中出现了无法刷新的异常报错&#xff0c; 检查后发现是由于原始数据的列的名字变化导致了power BI在处理数据类型的时候 需求 处理方法是什么&#xff0c; &#xff1f; 方法 动态获取表格的列&#xff1a; 获取数据的时候&#xff…

身份证识别接口的应用场景和作用

引言 在信息化与数字化高速发展的今天&#xff0c;身份证作为个人身份的重要证明文件&#xff0c;在各行各业的应用越来越广泛。传统的身份证信息录入和审核过程通常需要人工操作&#xff0c;不仅效率低下&#xff0c;而且容易出现错误。为了解决这些问题&#xff0c;身份证识别…

iftop流量监控工具

一、iftop简介 iftop可以用来监控网卡的实时流量&#xff08;可以指定网段&#xff09;、反向解析IP、显示端口信息等&#xff0c;详细的将会在后面的使用参数中说明。 二、安装iftop 1、编译安装 如果采用编译安装可以到iftop官网下载最新的源码包。 1.1 CentOS上安装所需…

学习之使用IDEA集成GIT

一、环境准备 1.1 配置git忽略文件 git.ignore 文件模版内容如下: # Compiled class file *.Class#Log file *.log# BlueJ file *.ctxt# Mobile Tools for Java (J2Me) *.mtj.tmp/# Package File *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar.classpath .project .settings…

基于SSM+Vue+MySQL的酒店管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着旅游业的蓬勃发展&#xff0c;酒店业作为旅游产业链中的重要一环&#xff0c;面临着日益增长的客户需求和激烈的市场竞争。传统的人工酒店管理模式已难以满足高效、精准、个性化的服务要求。因此&#xff0c;开发一套基于SS…

powerBi -L4-分组求和

有如下的表格&#xff1a;我们想统计 不同商品的销售次数&#xff0c;根据商品ID进行分类&#xff0c;统计不同ID出现的次数 1.新建列&#xff1a; 2.输入如下的公式 分组统计序列 COUNTROWS(FILTER(数据源,[商品类别]EARLIER(数据源[商品类别])&&[索引]<EARLIE…

算法学习1

知识点 一.时间复杂度二.排序1.选择排序2.冒泡排序 三.异或交换 一.时间复杂度 列如&#xff1a; //长度为n的整型数组 int arr[n];//循环1 for(int i 0 ;i < n; i) {for(int j i;j < n;j){cout << arr[i] << endl;cout << arr[j] << endl;}…

(c语言+数据结构链表)项目:贪吃蛇

目录 1.项目背景 2.游戏效果演⽰ 3. ⽬标 4. 技术要点 5. Win32 API介绍 5.1 Win32 API 5.2 控制台程序 5.3 控制台屏幕上的坐标COORD 5.4 GetStdHandle 5.5 GetConsoleCursorInfo 5.5.1 CONSOLE_CURSOR_INFO 5.6 SetConsoleCursorInfo 5.7 SetConsoleCursorPositi…