介绍一下常见的list实现类

题目详细答案

ArrayList

ArrayList 是最常用的 List 实现类,线程不安全,内部是通过数组实现的,继承了AbstractList,实现了List。它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。排列有序,可重复,容量不够的时候,新容量的计算公式为:

newCapacity = oldCapacity + (oldCapacity >> 1),这实际上是将原容量增加50%(即乘以1.5)。

ArrayList实现了RandomAccess接口,即提供了随机访问功能。RandomAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。

ArrayList实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

LinkedList(链表)

LinkedList 是用链表结构存储数据的,线程不安全。很适合数据的动态插入和删除,随机访问和遍历速度比较慢,增删快。另外,他还提供了 List 接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。底层使用双向链表数据结构。

LinkedList是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。

Vector(数组实现、线程同步)

Vector 与 ArrayList 一样,也是通过数组实现的,Vector和ArrayList用法上几乎相同,但Vector比较古老,一般不用。Vector是线程同步的,效率低。即某一时刻只有一个线程能够写 Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问 ArrayList 慢。默认扩展一倍容量。

ArrayList、LinkedList 与 Vector详解

在 Java 集合框架中,List 接口的实现类是日常开发中最常用的数据结构之一。其中,ArrayListLinkedListVector 是三种经典实现,它们基于不同的数据结构设计,适用于不同的业务场景。本文将从底层实现、核心特性、性能表现等方面详细对比这三种集合,帮助开发者合理选择。

一、ArrayList:基于动态数组的随机访问王者

ArrayListList 接口最常用的实现类,其设计核心是动态数组,兼顾了数组的高效访问和动态扩容能力。

底层实现

  • 内部通过Object类型数组存储元素,默认初始容量为10(JDK 8及以上)。
  • 当元素数量超过当前容量时,会触发扩容机制,新容量计算公式为:
    newCapacity = oldCapacity + (oldCapacity >> 1)(即原容量的1.5倍)。
  • 实现了 RandomAccess 接口(标记接口),表明支持快速随机访问(通过索引直接定位元素)。

核心特性

  1. 线程不安全:未实现同步机制,多线程并发修改时可能导致数据不一致(如 ConcurrentModificationException)。
  2. 有序可重复:元素按插入顺序排列,允许存储重复元素和 null 值。
  3. 随机访问高效:通过索引访问元素的时间复杂度为 O(1),适合频繁查询的场景。
  4. 增删效率较低
    • 尾部插入/删除:时间复杂度 O(1)(无扩容时)。
    • 中间插入/删除:需要复制和移动数组元素,时间复杂度 O(n)(n为元素数量)。
  1. 支持序列化:实现了 java.io.Serializable 接口,可通过序列化传输。

适用场景

  • 频繁通过索引访问元素(如随机读取操作多)。
  • 元素数量变化不大,或主要在尾部进行增删操作。
  • 单线程环境或已通过外部同步(如 Collections.synchronizedList)保证线程安全。

二、LinkedList:基于双向链表的灵活操作能手

LinkedList 采用双向链表作为底层结构,更注重元素插入和删除的灵活性。

底层实现

  • 内部由节点(Node) 组成,每个节点包含三部分:prev(前驱指针)、item(元素值)、next(后继指针)。
  • 不依赖数组,因此无需扩容,元素存储在离散的内存空间中。
  • 实现了 Deque 接口,可作为堆栈(Stack)队列(Queue)双端队列(Deque) 使用(提供 addFirst()pollLast() 等方法)。

核心特性

  1. 线程不安全:同 ArrayList,无同步机制,并发场景需额外处理。
  2. 有序可重复:元素按插入顺序排列,允许重复元素和 null 值。
  3. 随机访问低效:通过索引访问元素时,需从表头/表尾遍历,时间复杂度 O(n)
  4. 增删效率较高
    • 已知节点位置时,插入/删除仅需修改指针,时间复杂度 O(1)
    • 未知位置时,需先遍历找到节点,时间复杂度 O(n)
  1. 内存开销较大:每个元素需额外存储前驱和后继指针,内存占用高于 ArrayList

适用场景

  • 频繁在中间位置插入/删除元素(如实现链表式数据结构)。
  • 需要使用队列、堆栈等数据结构(利用 Deque 接口的方法)。
  • 元素数量不确定,且增删操作远多于查询操作。

三、Vector:古老的线程安全数组实现

Vector 是 Java 早期的 List 实现类,与 ArrayList 同属数组结构,但强调线程安全

底层实现

  • 内部通过Object类型数组存储元素,默认初始容量为10。
  • 扩容机制:默认新容量为原容量的2倍(可通过构造函数指定增量 capacityIncrement)。
  • 实现了 RandomAccess 接口,支持随机访问。

核心特性

  1. 线程安全:绝大多数方法(如 add()get())通过 synchronized 关键字修饰,保证多线程环境下的原子操作。
  2. 有序可重复:同 ArrayList,支持重复元素和 null 值。
  3. 性能较低:同步机制会带来额外开销,单线程环境下效率低于 ArrayList
  4. 功能冗余:部分方法(如 addElement()elementAt())与 List 接口方法重复,设计上较为陈旧。

适用场景

  • 多线程环境下需要线程安全的数组(但现代开发中更推荐 CopyOnWriteArrayList,性能更优)。
  • 维护 legacy 代码(旧系统中仍在使用)。

四、三者核心区别对比

特性

ArrayList

LinkedList

Vector

底层结构

动态数组

双向链表

动态数组

线程安全

不安全

不安全

安全(synchronized

随机访问效率

高(O(1))

低(O(n))

高(O(1))

中间增删效率

低(O(n))

高(已知位置时O(1))

低(O(n))

扩容机制

原容量1.5倍

无需扩容

原容量2倍(默认)

内存开销

较低(仅存储元素)

较高(需存储指针)

较低(同数组)

适用场景

频繁查询、尾部增删

频繁中间增删、队列/堆栈

多线程同步(不推荐新用)

五、选择建议

  1. 优先考虑 ArrayList:大多数场景下,查询操作远多于增删,且 ArrayList 的综合性能更优。
  2. 中间操作多选 LinkedList:当需要频繁在列表中间插入/删除元素,或需使用队列/堆栈功能时。
  3. 线程安全场景:避免使用 Vector,推荐 CopyOnWriteArrayList(读写分离,适合读多写少)或通过 Collections.synchronizedList 包装 ArrayList
  4. 容量规划:若已知元素数量,初始化时指定容量(如 new ArrayList<>(100)),可减少 ArrayListVector 的扩容次数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

埃泽漫笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值