Java高频面试之集合-03

hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶

面试官:说说ArrayList和LinkedList的区别

ArrayList 与 LinkedList 的详细对比

一、底层数据结构
特性 ArrayList LinkedList
存储结构 基于动态数组 基于双向链表
内存分配 连续内存块 非连续内存,节点分散存储
元素访问 通过索引直接寻址(时间复杂度 O(1)) 需要遍历链表(时间复杂度 O(n))
插入/删除 需要移动元素(时间复杂度 O(n)) 修改指针(时间复杂度 O(1),但需先遍历到位置)
空间开销 仅存储数据和数组容量,内存紧凑 每个节点额外存储前驱和后继指针,内存占用更高

二、核心操作性能对比
1. 随机访问(Get/Set)
  • ArrayList

    • 直接通过索引访问数组元素,时间复杂度 O(1)

    • 示例代码:

      java 复制代码
      list.get(1000);  // 直接定位到数组第1000个位置
  • LinkedList

    • 需要从链表头部或尾部遍历到目标位置,时间复杂度 O(n)

    • 优化技巧:根据索引位置选择从头或尾遍历(如 index < size/2 从头开始)。

    • 示例代码:

      java 复制代码
      list.get(1000);  // 需要遍历至少1000个节点
2. 插入与删除
  • 尾部插入(Add)

    • ArrayList
      • 如果数组未满,直接添加到末尾,时间复杂度 O(1)
      • 如果数组已满,需扩容(通常扩容为原容量的1.5倍)并复制数据,均摊时间复杂度 O(1)
    • LinkedList
      • 直接修改尾节点的指针,时间复杂度 O(1)(无需遍历)。
  • 中间插入(Add at Index)

    • ArrayList
      • 需要将插入位置后的元素后移,时间复杂度 O(n)
      • 示例:在索引 i 处插入元素,需移动 n - i 个元素。
    • LinkedList
      • 先遍历到目标位置(时间复杂度 O(n)),再修改指针(O(1)),总体时间复杂度 O(n)
  • 删除(Remove)

    • ArrayList
      • 类似插入操作,需将后续元素前移,时间复杂度 O(n)
    • LinkedList
      • 遍历到目标节点后修改指针,时间复杂度 O(n)(遍历是主要开销)。

三、内存占用与扩容机制
1. 内存占用
  • ArrayList

    • 内存连续,仅存储数据和数组容量字段。
    • 每个元素占用空间 = 数组槽位大小(如 Integer 为4字节)。
    • 示例:存储1000个整数,数组容量为1000时,总内存 ≈ 1000 * 4B = 4KB。
  • LinkedList

    • 每个节点包含数据、前驱指针、后继指针,内存分散。
    • 每个节点内存开销 ≈ 对象头(12B) + 3个引用(各4B) + 数据 = 至少 24B。
    • 示例:存储1000个整数,总内存 ≈ 1000 * 24B = 24KB(是 ArrayList 的6倍)。
2. 扩容机制
  • ArrayList

    • 默认初始容量为10,扩容时创建新数组并复制数据。
    • 扩容公式:newCapacity = oldCapacity + (oldCapacity >> 1)(即1.5倍)。
    • 扩容代价高,但均摊时间复杂度仍为 O(1)。
  • LinkedList

    • 无扩容概念,每次插入动态创建新节点。
    • 无内存浪费,但频繁插入可能触发GC(节点对象创建/销毁)。

四、应用场景
适合使用 ArrayList 的场景
  1. 频繁随机访问
    • 例如:按索引读取或修改元素(如 list.get(i))。
  2. 尾部插入/删除
    • 例如:日志记录、批量数据处理。
  3. 内存敏感场景
    • 需存储大量数据且希望减少内存占用。
适合使用 LinkedList 的场景
  1. 频繁在头部或中间插入/删除
    • 例如:实现栈(Stack)、队列(Queue)或双向队列(Deque)。
  2. 动态调整数据规模
    • 无需担心扩容问题,适合元素数量变化较大的场景。
  3. 需要实现复杂数据结构
    • 如LRU缓存(通过双向链表快速移动节点)。

五、代码示例与性能测试
1. 尾部插入性能对比
java 复制代码
// ArrayList
List<Integer> arrayList = new ArrayList<>();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
    arrayList.add(i);  // 均摊 O(1)
}
System.out.println("ArrayList 尾部插入耗时: " + (System.currentTimeMillis() - start) + "ms");

// LinkedList
List<Integer> linkedList = new LinkedList<>();
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
    linkedList.add(i);  // O(1)
}
System.out.println("LinkedList 尾部插入耗时: " + (System.currentTimeMillis() - start) + "ms");

结果

  • ArrayList 通常更快(因内存连续,CPU缓存友好)。
  • LinkedList 可能因频繁创建节点对象导致GC开销。
2. 中间插入性能对比
java 复制代码
// ArrayList
start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
    arrayList.add(0, i);  // 每次插入需移动所有元素,O(n)
}
System.out.println("ArrayList 头部插入耗时: " + (System.currentTimeMillis() - start) + "ms");

// LinkedList
start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
    linkedList.add(0, i);  // O(1)
}
System.out.println("LinkedList 头部插入耗时: " + (System.currentTimeMillis() - start) + "ms");

结果

  • LinkedList 明显优于 ArrayList(避免元素移动)。

六、高级特性与注意事项
  1. 迭代器性能

    • ArrayList:迭代器直接通过索引访问,性能高。
    • LinkedList :迭代器需逐个遍历节点,性能较低(但实现了 ListIterator,支持双向遍历)。
  2. 线程安全性

    • 两者均非线程安全,多线程环境下需使用 Collections.synchronizedListCopyOnWriteArrayList
  3. 序列化与克隆

    • ArrayList 重写了 clone(),实现浅拷贝。
    • LinkedList 的克隆需要遍历复制所有节点。

🐮👵

维度 ArrayList LinkedList
数据结构 动态数组 双向链表
访问效率 O(1)(随机访问) O(n)
插入/删除 O(n)(需移动元素) O(1)(已知位置)或 O(n)(需遍历)
内存占用 低(连续存储) 高(每个节点额外指针开销)
适用场景 随机访问、尾部操作、内存敏感 频繁头部/中间插入删除、实现队列/栈

选择建议

  • 优先使用 ArrayList(90%场景更优)。
  • 仅在需要频繁在 头部或中间插入/删除 ,或实现 队列/栈 时选择 LinkedList。
相关推荐
Jelena157795857922 分钟前
爬虫与翻译API接口的完美结合:开启跨语言数据处理新纪元
开发语言·数据库·爬虫
gyc272730 分钟前
快速熟悉JavaScript
开发语言·前端·javascript
越甲八千34 分钟前
C++海康相机DEMO
开发语言·c++·数码相机
0白露1 小时前
PHP之特性
开发语言·php
曹天骄1 小时前
Spring Boot Gradle 项目中使用 @Slf4j 注解
java·spring boot·后端
青春_strive1 小时前
Qt:事件
开发语言·qt
A仔不会笑1 小时前
Redis面试常见问题——集群方案
数据库·redis·面试
虾球xz1 小时前
游戏引擎学习第133天
java·学习·游戏引擎
吉星9527ABC1 小时前
Linux下的c进程和java进程的通信-UnixSocket
java·linux·c语言·unixsocket通讯
浪九天1 小时前
Java常用正则表达式(身份证号、邮箱、手机号)格式校验
java·开发语言·正则表达式