当前位置: 首页 > news >正文

List | 常见的List实现类(ArrayList、LinkedList、Vector)以及ArrayList源码解读

目录

List        

ArrayList

LinkedList

Vector

ArrayList源码解读

ArrayList的初始容量

ArrayList的扩容机制

相关问题

ArrayList与LinkedList添加元素速度对比


List        

        在集合框架中,List是一个接口,它继承了Collection接口,Colletion接口继承了Iterable接口。Collection接口规范了后续容器中的一些常用方法,例如size()、add()、contains()等等,Iterable接口表示实现该接口的类是可以逐个元素进行遍历的。站在数据结构的角度看,List是一个线性表,即n个具有相同类型元素的有限序列,在该序列上可以执行增删查改等操作。List是一个接口,我们不能直接使用List来实例化,必须使用其实现类来实例化,例如List<String> list = new ArrayList<>()。

        其中ArrayList、LinkedList、Vector都是其常见的实现类,本篇博客针对上述三个实现类进行介绍,并对比ArrayList和LinkedList的区别。

ArrayList

        ArrayList的底层是基于数组实现的,该类继承了AbstractList类,实现了List、RandomAccess、Cloneable、Serializable接口。

        其实现了RandomAccess接口,说明其支持随机访问;实现了Cloneable接口,说明ArrayList是支持克隆的;实现了Serializable接口,说明其是可以被序列化的。

        ArrayList是线程不安全的,其内部是通过数组实现的,数组的缺点是每个元素之间不能有间隔,当数组大小不满足需求时,就需要对数组进行扩容,而扩容操作需要新建一个数组,然后将当前数组中的元素复制到新数组中。如果在ArrayList中间位置进行插入或者删除元素时,需要对数组进行复制、移动,效率比较低,因此ArrayList适合随机查找元素的场景,不适用于插入和删除频繁的场景。

LinkedList

        LinkedList的底层是基于双向链表实现的,该类继承了AbstractSequentialList类,实现了List、Deque、Cloneable、Serializable接口。

        LinkedList是线程不安全的,其底层是基于双向链表实现的,适合数据插入和删除操作频繁的场景,插入和删除数据的操作很快,但其随机访问和遍历的速度较慢,它还提供了List接口没有定义的方法用于操作表头和表尾的元素。

Vector

        Vector与ArrayList一样,也是通过数组实现的,Vector的用法也与ArrayList是一样的。但Vector是线程同步的,也就是线程安全的,这样在单线程情况下效率比较低,但在多线程环境下使用Vector可以解决线程安全问题。其扩容大小也与ArrayList不一样,其默认是扩容一倍,即初始容量是10,当出发扩容机制后,扩容后的容量为20。

        之所以说它是线程安全的,因为其读写这些操作都是同步的,即加了synchronized关键字。

ArrayList源码解读

ArrayList的初始容量

        在Java中,ArrayList的初始容量严格来说就是10,但是在JDK1.8版本及之后,如果我们在实例化ArrayList类时不对其指定初始容量大小,那么它的数组最开始是一个空数组,当我们对其放入第一个元素时,就会触发其懒加载机制,使数组的初始容量变为10,这种机制可以优化性能和内存。其空数组是使用static和final修饰的,是所有实例所共享的,这种做法可以有效的减少内存消耗,只有在需要时才对数组分配内存。例如我们实例化了1000个ArrayList对象但只使用了其中10个,如果我们不对其使用这种懒加载机制,就会创建1000个大小为10的数组,会浪费很多内存空间,而使用了这种懒加载机制,就会有效的节省内存消耗提高性能。源码如下所示:

        通过下列源码可以看出,如果对其指定了初始容量,这个初始容量如果大于0,就按指定的容量创建底层的数组,如果等于0就使用空数组,小于0抛出异常;不对其指定初始容量,那这个数组一开始就也是空数组。

        通过下列源码可以看出,当往ArrayList放入元素时,会先检查其数组的容量,例如我们第一次往ArrayList中添加元素,此时size是0,那么传入到ensureCapacityInternal的minCapacity就是1,此时根据判断条件,就会给数组初始化容量为10。

ArrayList的扩容机制

ArrayList的扩容机制主要可以分为以下几个步骤:

        · 初始容量和扩容因子:初始容量即上一节所描述的部分,其扩容因子是用来计算新容量的一个乘数,默认为1.5。如下图源码所示:

        

        · 扩容触发条件:当向ArrayList中添加一个新元素,并且添加该元素后的数量超过当前数组的容量时,就会触发扩容操作。如下图源码所示,此处的minCapacity即插入新元素前的数组元素个数+1:

        · 扩容策略:扩容时首先根据当前容量和扩容因子计算出一个新的容量。比如初始容量是10,当触发扩容条件后,根据当前的容量和扩容因子,那么扩容后的容量即10 × 1.5,即15。如果所需的容量大于Integer.MAX_VALUE - 8,则会默认使用Integer.MAX_VALUE作为数组的大小。这里-8是因为在内存中除了存储数据元素外,还需要存储元数据信息(对象头、数组长度)

        · 扩容过程:①创建一个新的数组,其长度未新计算的容量;②将原数组中的所有元素复制到新数组中;③将ArrayList的内部引用从原数组更改为新数组;④将新元素插入到新数组的末尾。

相关问题

ArrayList与LinkedList添加元素速度对比

        这里可以分为在首部添加、尾部添加、中间添加三种情况:

        · 首部添加:在首部添加元素时,ArrayList需要将原数组的所有元素后移一个位置,还可能牵扯到扩容操作,因此效率相较于LinkedList效率较低;LinkedList在首部添加元素只需要创建一个新的结点,再改变指针指向即可,时间复杂度为O(1),效率较高。

        · 尾部添加:尾部添加又可以分为两种情况,一种是要插入的元素数据量较大时,一种是数据量较小时。当数据量较小时,ArrayList会涉及到频繁的扩容操作,可能添加元素的速度稍微慢一点,而LinkedList只需要创建一个并调整指针指向;当数据量较大时,ArrayList的扩容次数可以减少很多,此时在尾部添加元素时效率可能高于LinkedList,因为LinkedList每次add操作都需要创建一个结点,这可能会增加时间消耗。

        · 中间添加对于ArrayList来说,在中间插入元素,需要将该位置以及之后的元素都后移一位腾出空间然后插入,其时间复杂度为O(n),其中n为数组长度。但插入位置越靠后,需要复制移动的元素越少,效率越高。对于LinkedList来说,在中间插入元素需要遍历链表找到插入位置,需要从链表两端向中间搜索寻找要插入的位置,index越靠中间,需要遍历的次数就越多,效率也相对较低。但当数据量较小时,LinkedList的性能可能会超过ArrayList,因为ArrayList在数据量小时会涉及到频繁的扩容操作。

        

http://www.dtcms.com/a/354371.html

相关文章:

  • 【Redis】数据分片机制和集群机制
  • 8.28 模拟|双指针
  • 零基础-力扣100题从易到难详解(持续更新1-10题)
  • Windows 命令行:rmdir 命令
  • Qt 6 与 Qt 5 存在的兼容性差异
  • C# 数组C# 多维数组
  • QML(2) - Qt 中如何注册一个 C++ 类到 QML
  • MySQL的类split方法实现
  • Java表格处理详解以及结合实际项目使用
  • WebStrom-如何设置前端项目快捷启动
  • 29. String, StringBuffer,StringBuilder 的区别是什么
  • 统一虚拟试穿框架OmniTry:突破服装局限,实现多品类可穿戴物品虚拟试穿无蒙版新跨越。
  • 【小白笔记】网速
  • TypeScript:完整的函数类型书写方式
  • 【开题答辩全过程】以超市管理系统为例,包含答辩的问题和答案
  • Linux 系统核心调优:CPU、磁盘 I/O、网络与内核参数实战
  • 流行蝴蝶剑高清重制版Windows10可玩!
  • 小程子找Bug之for循环的初始化表达类型
  • 【美团】放它一马
  • 今日行情明日机会——20250827
  • 即时配送运营平台系统功能分析
  • 寄存器, 堆栈, 汇编指令详解
  • 入门概念|Thymeleaf与Vue
  • 企业微信对接 代理 WXJava Ngnix映射 weixin-java-cp
  • Vue 登录页高低分辨率背景图优化实现
  • mathtype公式存在乱码
  • SqlHelper类的方法详细解读和使用示例
  • Libvio访问异常排查指南
  • sql server 取起始日期到结束日期中自然月最后一天,与日期维度行转列
  • 数据治理:AI健康血液的生命线