当前位置: 首页 > 技术教程

Java 集合 List 怎么使用? ArrayList 和 LinkedList 哪个好?

  List 是 Java 集合框架中最常用的接口之一,它继承自 Collection 接口,具有有序性(元素按插入顺序排列)和可重复性(允许存储相同元素)的特点,支持通过索引位置访问和操作元素。ArrayList 和 LinkedList 是 List 接口的两个主要实现类,各自基于不同的数据结构,适用场景存在差异。掌握 List 的基本用法及两者的区别,能帮助开发者在实际开发中选择更高效的集合实现。

  一、Java 集合 List 的基本使用

  List 接口的使用需先创建实现类的对象(如 ArrayList 或 LinkedList),再通过接口定义的方法操作元素,核心方法包括添加、删除、查询、修改等。

  (一)创建 List 对象

  // 导入List和ArrayList/LinkedList所在包import java.util.List;import java.util.ArrayList;import java.util.LinkedList;// 创建ArrayList对象(基于动态数组)List<String> arrayList = new ArrayList<>();// 创建LinkedList对象(基于双向链表)List<Integer> linkedList = new LinkedList<>();

  (二)常用方法及示例

  添加元素:add(E e)(末尾添加)、add(int index, E e)(指定位置插入)

  List<String> list = new ArrayList<>();list.add("苹果"); // 末尾添加:[苹果]list.add(0, "香蕉"); // 索引0插入:[香蕉, 苹果]

  获取元素:get(int index)(通过索引获取)

  String fruit = list.get(1); // 获取索引1的元素:"苹果"

  修改元素:set(int index, E e)(替换指定位置元素)

  list.set(0, "橙子"); // 替换索引0的元素:[橙子, 苹果]

  删除元素:remove(int index)(删除指定位置元素)、remove(Object o)(删除指定元素)

  list.remove(1); // 删除索引1的元素:[橙子]list.remove("橙子"); // 删除元素"橙子":[]

  其他常用方法:

  size():返回元素个数;

  isEmpty():判断是否为空;

  contains(Object o):判断是否包含指定元素;

  clear():清空所有元素。

图片2.png

  二、ArrayList 与 LinkedList 的核心区别

  ArrayList 和 LinkedList 均实现了 List 接口,但底层数据结构不同,导致两者在性能、内存占用等方面存在显著差异。

  (一)底层数据结构

  ArrayList:基于动态数组实现,元素存储在连续的内存空间中,通过索引直接访问(类似数组,但容量可动态扩容)。

  LinkedList:基于双向链表实现,元素存储在非连续的节点中,每个节点包含前驱、后继指针和元素值,需通过指针遍历访问。

  (二)性能对比

  随机访问(get/set 操作):

  ArrayList 优势明显:通过索引直接定位元素,时间复杂度为 O(1)(常数级);

  LinkedList 效率低:需从链表头或尾遍历至目标索引,时间复杂度为 O(n)(线性级),元素越多,性能差距越大。

  插入 / 删除操作:

  头部 / 中间插入 / 删除:LinkedList 更优,只需修改节点指针,时间复杂度 O(1)(找到节点后);ArrayList 需移动插入 / 删除位置后的所有元素,时间复杂度 O(n)。

  尾部插入 / 删除:两者性能接近,ArrayList 直接在末尾添加(若无需扩容),LinkedList 只需修改尾节点指针。

  内存占用:

  ArrayList 内存占用较紧凑,仅存储元素本身(动态数组可能预留部分容量,存在少量内存浪费);

  LinkedList 每个元素需额外存储前后指针,内存开销更大。

  (三)适用场景

  优先选择 ArrayList:

  频繁进行随机访问(如通过索引获取元素);

  元素数量稳定,插入 / 删除操作主要在尾部;

  对内存占用较敏感的场景。

  优先选择 LinkedList:

  频繁在头部、中间进行插入 / 删除操作(如实现队列、栈等数据结构);

  元素数量动态变化大,且插入 / 删除操作密集。

  示例:性能差异体现

  // 场景1:随机访问密集List<Integer> arrayList = new ArrayList<>();for (int i = 0; i < 10000; i++) { arrayList.add(i);}long start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) { arrayList.get(i); // 高效:直接索引访问}System.out.println("ArrayList随机访问耗时:" + (System.currentTimeMillis() - start) + "ms");// 场景2:中间插入密集List<Integer> linkedList = new LinkedList<>();start = System.currentTimeMillis();for (int i = 0; i < 1000; i++) { linkedList.add(0, i); // 高效:头部插入只需修改指针}System.out.println("LinkedList头部插入耗时:" + (System.currentTimeMillis() - start) + "ms");

  三、使用注意事项

  ArrayList 扩容机制:默认初始容量为 10,当元素满时自动扩容至原来的 1.5 倍(通过复制数组实现),频繁扩容会影响性能。若已知元素数量,可在创建时指定容量(如new ArrayList<>(1000)),减少扩容次数。

  LinkedList 的遍历效率:使用增强 for 循环(foreach)或迭代器遍历 LinkedList 效率较高,避免通过get(index)遍历(性能极差)。

  线程安全性:两者均为非线程安全集合,多线程环境下需通过Collections.synchronizedList(List)包装,或使用CopyOnWriteArrayList替代。

  List 接口的使用需结合具体场景选择实现类:ArrayList 适合随机访问为主的场景,LinkedList 适合插入 / 删除密集的场景,不存在绝对的 “更好”,只有 “更合适”。掌握两者的底层原理和性能特性,能帮助开发者写出更高效的代码,避免因选择不当导致的性能瓶颈。

 


猜你喜欢