Java中ArrayList与LinkedList的区别是什么?
ArrayList 是基于 动态数组 的,它查找数据的速度很快,但 插入 和 删除 数据时会比较慢;而 LinkedList 是基于 双向链表 的,插入和删除数据时速度比较快,但查找数据时比较慢。两者各有优缺点,我们需要根据实际情况来选择合适的方式。
📚 知识内容
🧩 1. ArrayList 的核心特点
🌳 底层实现
数据结构:ArrayList 的底层基于 动态数组。
扩容机制:
初始容量为 10,当插入的数据超过当前容量时,扩容为原容量的 1.5 倍。
扩容时会创建一个新数组,并将旧数组的数据拷贝到新数组中。
💡 优缺点
优点:
查询速度快:因为数组支持随机访问,时间复杂度为 O(1)。
内存利用率高:数组是连续存储的,内存分配效率高。
缺点:
插入和删除效率低:在中间插入或删除数据时,需要移动大量数据,时间复杂度为 O(n)。
扩容开销大:扩容时需要重新分配内存并复制数据,性能损耗较高。
🛠️ 代码示例:常见操作
import java.util.ArrayList;
public class ArrayListExample {
public static void main(String[] args) {
ArrayList
// 添加元素
list.add("Java");
list.add("Python");
list.add("C++");
System.out.println("初始列表: " + list); // 输出: [Java, Python, C++]
// 随机访问
System.out.println("第一个元素: " + list.get(0)); // 输出: Java
// 在索引位置插入
list.add(1, "Go");
System.out.println("插入后列表: " + list); // 输出: [Java, Go, Python, C++]
// 删除元素
list.remove("Python");
System.out.println("删除后列表: " + list); // 输出: [Java, Go, C++]
}
}
🌳 2. LinkedList 的核心特点
🌿 底层实现
数据结构:LinkedList 是基于 双向链表 实现的。每个节点包含三个部分:
prev:指向前一个节点。
data:存储数据。
next:指向后一个节点。
插入和删除操作只需要调整节点指针,而无需像 ArrayList 一样移动数据。
💡 优缺点
优点:
插入和删除效率高:时间复杂度为 O(1),特别是在头部或尾部插入和删除时。
无需扩容:链表是动态扩展的,存储容量不受限制。
缺点:
查询速度慢:由于无法随机访问,需要从头开始遍历,时间复杂度为 O(n)。
内存开销高:每个节点都需要额外存储两个指针,占用更多内存。
🛠️ 代码示例:常见操作
import java.util.LinkedList;
public class LinkedListExample {
public static void main(String[] args) {
LinkedList
// 添加元素
list.add("Java");
list.add("Python");
list.add("C++");
System.out.println("初始列表: " + list); // 输出: [Java, Python, C++]
// 在头部插入元素
list.addFirst("Go");
System.out.println("头部插入后: " + list); // 输出: [Go, Java, Python, C++]
// 在尾部插入元素
list.addLast("Ruby");
System.out.println("尾部插入后: " + list); // 输出: [Go, Java, Python, C++, Ruby]
// 删除元素
list.remove("Python");
System.out.println("删除后列表: " + list); // 输出: [Go, Java, C++, Ruby]
}
}
🌟 3. ArrayList 和 LinkedList 的性能对比
操作类型 ArrayList LinkedList
查询操作 快速(O(1)) 较慢(O(n))
插入/删除操作 慢(O(n),需要移动元素) 快(O(1),只调整指针)
内存占用 内存占用低,数据连续存储 内存占用高,需额外存储前后指针
扩展性 需要扩容,扩容会影响性能 无需扩容,动态分配节点
🔍 知识拓展
1️⃣ 线程安全的 List 实现
在并发环境下,ArrayList 和 LinkedList 是非线程安全的。如果需要线程安全的 List,可以使用以下方案:
🌿 CopyOnWriteArrayList
特点:
每次写操作都会复制一份数组副本,保证线程安全。
适合 读多写少 的场景,比如缓存、配置读取。
🛠️ 示例代码
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
CopyOnWriteArrayList
list.add(“Java”);
list.add(“Python”);
for (String lang : list) {
list.add("C++"); // 不会抛出 ConcurrentModificationException
System.out.println(lang);
}
System.out.println("最终列表: " + list); // 输出: [Java, Python, C++]
}
}
2️⃣ 优化建议
场景选择建议:
查询频繁、数据量大时:选择 ArrayList。
插入或删除操作频繁时:选择 LinkedList。
并发场景:选择 线程安全的集合类,如 CopyOnWriteArrayList。
减少扩容次数:
使用 ArrayList 时,可以通过构造函数设置初始容量,减少扩容开销。
ArrayList
避免使用非线程安全集合类:
在多线程场景中,非线程安全集合会导致数据不一致问题,需使用同步包装:
List
🎉 总结
ArrayList 和 LinkedList 各有优缺点:
ArrayList 更适合 查询频繁、数据量大的场景。
LinkedList 更适合 插入和删除操作频繁 的场景。
深入理解两者的底层实现和性能特性,可以帮助我们在开发中合理选择,避免性能瓶颈。
希望这篇文章可以让你轻松掌握它们的区别,开发中更得心应手!🚀
