Java中ArrayList、LinkedList和Vector的底层原理

devtools/2024/9/23 22:33:16/

ArrayList

Java中的ArrayList底层原理主要涉及其数据结构、扩容机制、线程安全性以及元素存储和访问方式。以下是对ArrayList底层原理的总结:

数据结构

ArrayList的底层数据结构是一个动态数组。这意味着ArrayList可以根据需要自动增长其容量,从而存储更多的元素。实际上,ArrayList内部维护了一个Object类型的数组,用于存储列表中的元素。

扩容机制

当向ArrayList中添加元素时,如果当前数组的容量不足以容纳新元素,ArrayList会自动进行扩容。扩容操作涉及创建一个新的、容量更大的数组,并将原数组中的元素复制到新数组中。为了减少扩容时数据的拷贝次数,ArrayList在扩容时通常会选择一个比当前容量大的多的新容量(通常是当前容量的1.5倍)。
ArrayList的默认初始容量为10,但你可以在创建ArrayList实例时指定一个初始容量。如果你知道将要存储的元素数量,提前指定一个合适的初始容量可以减少扩容时数据的拷贝次数,从而提高性能。

线程安全性

ArrayList是非线程安全的。这意味着如果多个线程同时修改ArrayList(例如,一个线程在遍历列表时,另一个线程在添加或删除元素),可能会导致数据不一致或其他并发问题。因此,在多线程环境中使用ArrayList时,需要进行额外的同步操作来确保线程安全。

元素存储和访问

由于ArrayList底层是一个数组,因此元素的存储和访问都基于数组的索引。添加元素时,ArrayList会将新元素存储在数组的末尾(或指定的索引位置),并更新列表的大小。访问元素时,ArrayList会根据提供的索引直接访问数组中的相应位置。由于数组在内存中是连续存储的,因此访问数组元素的时间复杂度是O(1)。
然而,由于ArrayList在添加元素时可能需要扩容并复制数组,因此在列表的中间位置添加或删除元素的时间复杂度可能较高(最坏情况下为O(n))。相比之下,在列表的开头或结尾添加或删除元素的时间复杂度通常是O(1),因为这两个位置的操作不需要移动其他元素。

总的来说,ArrayList是一个功能强大且灵活的数据结构,适用于需要在内存中存储和访问大量元素的情况。但是,在多线程环境中使用时需要注意线程安全问题

LinkedList

Java中的LinkedList底层原理主要基于双向链表数据结构。以下是关于LinkedList底层原理的总结:
数据结构

LinkedList使用双向链表作为其内部数据结构。双向链表中的每个节点(Node)都包含三个部分:
元素值(item):存储链表中的实际数据。
前节点引用(prev):指向链表中当前节点之前的节点。对于链表的第一个节点,此引用通常为null。
后节点引用(next):指向链表中当前节点之后的节点。对于链表的最后一个节点,此引用通常为null。

插入操作

在 LinkedList 中,增加操作同样依赖于其双向链表的特性。当你尝试向列表中添加一个新元素时,LinkedList 实际上会:

  1. 确定插入位置:根据提供的索引或方法(如 addFirst、addLast),LinkedList 会确定新元素应该插入的位置。
  1. 创建新节点:LinkedList 会创建一个新的节点(通常是一个内部类),该节点包含要添加的元素以及指向其前一个和后一个元素的引用(最初这些引用可能为空或指向其他元素)。
  1. 更新链接:根据插入位置,LinkedList 会更新新节点与其前一个和后一个元素的链接。具体来说,它会将新节点的 prev 引用指向前一个元素(如果存在的话),将新节点的 next 引用指向后一个元素(如果存在的话),并相应地更新前一个和后一个元素的引用。
  1. 维护大小:LinkedList 还会更新其内部的大小计数器,以反映列表中元素数量的增加。

注意事项

1.由于 LinkedList 是双向链表,因此在列表的任何位置进行删除或增加操作的时间复杂度都是 O(n),其中 n 是列表的大小。但是,由于不需要移动元素(如 ArrayList 在列表中间进行删除或增加时所做的那样),这些操作通常更快。
2.LinkedList 还提供了在列表开头和结尾进行高效删除和增加操作的方法(如 addFirst、addLast、removeFirst、removeLast),这些操作的时间复杂度是 O(1)。
3.由于 LinkedList 需要额外的空间来存储每个元素的引用,因此它在内存使用方面可能比基于数组的列表更高。

删除操作
在LinkedList中删除元素时,需要找到要删除的节点,并更新其前后节点的引用以断开连接。

  1. 删除头部节点:将头节点引用更新为其next节点,并设置新头节点的prev引用为null(如果新头节点不是null的话)。
    删除尾部节点:遍历链表找到倒数第二个节点,将其next引用设置为null。
  1. 删除指定索引的节点:遍历链表找到指定索引的节点,然后更新其前后节点的引用以断开连接。

访问操作

访问LinkedList中的元素通常需要通过遍历链表来实现,因为链表中的元素在内存中不是连续存储的。但是,由于LinkedList同时提供了向前和向后遍历的能力(通过ListIterator),因此访问操作在某些情况下可能比其他链表结构更高效。

线程安全性

与ArrayList和Vector不同,LinkedList本身不是线程安全的。如果多个线程同时修改LinkedList,则需要在外部进行同步操作以确保线程安全。但是,由于LinkedList的双向链表结构,它在并发修改时通常比基于数组的列表(如ArrayList)具有更好的性能。

性能特点

插入和删除操作:在链表的开头或结尾进行插入和删除操作的时间复杂度通常为O(1),因为只需要修改几个引用即可。在链表的中间进行插入和删除操作的时间复杂度为O(n),其中n是链表的长度,因为需要遍历链表以找到正确的位置。
访问操作:访问链表中的特定元素(如通过索引)需要遍历链表,因此其时间复杂度为O(n)。
空间效率:由于每个节点都需要额外的空间来存储引用(prev和next),因此LinkedList的空间效率略低于基于数组的列表(如ArrayList)。但是,这种差异在大多数情况下都是可以接受的,特别是当插入和删除操作的性能更重要时。

Vector

Java中的Vector类的底层原理主要涉及其数据结构、扩容机制、线程安全性以及元素存储和访问方式。以下是对Vector底层原理的总结:
数据结构

Vector的底层数据结构同样是一个动态数组。它内部维护了一个Object类型的数组,用于存储列表中的元素。与ArrayList类似,Vector也可以根据需要动态地增长或缩小其容量。

扩容机制>
当向Vector中添加元素时,如果当前数组的容量不足以容纳新元素,Vector会自动进行扩容。扩容操作涉及创建一个新的、容量更大的数组,并将原数组中的元素复制到新数组中。扩容的倍数通常是原容量的两倍,但具体实现可能因Java版本或JVM实现而异。

线程安全性

Vector是一个线程安全的类。它的所有方法(包括添加、删除、获取元素等操作)都是同步的,这意味着在多线程环境下,多个线程可以同时访问和修改Vector对象,而不会产生数据竞争和不一致的问题。然而,这种线程安全性是以牺牲性能为代价的,因为同步操作会引入额外的开销。

元素存储和访问

与ArrayList一样,Vector使用数组的索引来存储和访问元素。添加元素时,Vector会将新元素存储在数组的末尾(或指定的索引位置),并更新列表的大小。访问元素时,Vector会根据提供的索引直接访问数组中的相应位置。

性能考虑

虽然Vector提供了线程安全性,但在单线程环境下,由于同步操作的开销,它的性能通常不如ArrayList。因此,在不需要线程安全性的情况下,使用ArrayList通常是一个更好的选择。如果需要线程安全性,可以考虑使用Collections.synchronizedList()方法将ArrayList包装为线程安全的列表,或者使用CopyOnWriteArrayList等并发集合类。

总的来说,Vector是一个基于动态数组实现的线程安全的列表类。然而,由于同步操作的开销和性能考虑,它在现代Java编程中逐渐被其他并发集合类所取代。


http://www.ppmy.cn/devtools/39631.html

相关文章

Linux下安装mysql8.0(以rpm包安装)

前言:原文在我的博客网站中,持续更新数通、系统方面的知识,欢迎来访! Linux下安装mysql8.0(以rpm包安装)https://myweb.myskillstree.cn/125.html 目录 1、查操作系统信息 2、下载mysql 8.0.34的rpm包 …

纯血鸿蒙APP实战开发——自定义视图实现Tab效果

介绍 本示例介绍使用Text、List等组件,添加点击事件onclick,动画,animationTo实现自定义Tab效果。 效果预览图 使用说明 点击页签进行切换,选中态页签字体放大加粗,颜色由灰变黑,起到强调作用,同时&…

高速、简单、安全的以太彩光,锐捷网络发布极简以太全光 3.X 方案

从 2021 年 3 月正式推出到现在,锐捷网络极简以太全光方案已经走进第四个年头。IT 仍在不断向前发展,数字化进程深入,数字化业务增多,更广泛的终端设备接入企业级园区网络,对园区网络提出了更高的要求,例如…

【C++】n个一位数能够组成的最大数

文章目录 题目题目描述输入输出样例输入样例输出 思路AC代码 题目 题目描述 请问n个一位数能够组成的最大的整数是多少。 比如, n 3 n3 n3,3个整数为 1 、 3 、 9 1、3、9 1、3、9,那么组成的最大整数是 931 931 931。 比如, n…

leetcode——链表的中间节点

876. 链表的中间结点 - 力扣(LeetCode) 链表的中间节点是一个简单的链表OJ。我们要返回中间节点有两种情况:节点数为奇数和节点数是偶数。如果是奇数则直接返回中间节点,如果是偶数则返回第二个中间节点。 这道题的解题思路是&a…

Vulnhub项目:ICA: 1

1、靶机介绍 靶机地址:ICA: 1 ~ VulnHub 2、渗透过程 首先,部署好靶机后,进行探测,发现靶机ip和本机ip,靶机ip156,本机ip146。 然后查看靶机ip有哪些端口,nmap一下。 出现22、80、3306端口&a…

代码质量检查jacoco环境搭建

这里主要介绍集成和系统测试覆盖率环境搭建,并简单介绍各个工具。 关于单元测试的覆盖率监控(只需要修改ant或maven配置即可),下一篇说明 环境准备 需要环境 jdk1.8centos 7posgresql 9.6 工具下载 jacoco 0.8.2 https://www.eclemma.org/jacoco/ a…

vite创建的项目使用rem适配

下面以创建vue3.0 项目为例: npm init vitelatest “名称” 选择vue (选择你所对应的语言) 更具提示步骤执行 cd xxx npm i npm run dev 然后再项目中使用 rem 需要安装插件 第一步安装插件 npm i amfe-flexible npm i postcss-pxtorem 第二…