1. 介绍
List 在 Java 中的重要性是什么?
在 Java 编程中,List 是一种非常常见的数据结构。尤其在需要一起管理多个值的场景下,它比数组更灵活、使用更方便,因而在许多实际场景中备受青睐。
List 是 Java 集合框架中的核心接口,通过 ArrayList、LinkedList 等不同实现类可以应对各种情况。能够直观地进行添加、删除、查找、更新等操作,是 List 受欢迎的主要原因之一。
本文的目的与受众
本文将系统地、通俗易懂地为初学者讲解 “Java List”,从基础到进阶。主要受众如下:
- 刚开始学习 Java、对如何使用 List 还不清楚的读者
- 想要明确了解 Array 与 List 区别的读者
- 在 ArrayList 与 LinkedList 之间犹豫不决的读者
- 想在实际使用 List 前复习基础知识的读者
阅读完本文后,我们的目标是让你对 Java 中 List 的基本概念、实现方式以及具体操作有扎实的理解,从而自信地编写代码。
接下来的章节将从最基础的 “List 是什么?” 开始,逐步展开讲解。
2. List 是什么?
List 的概述与特性
Java 中的 List 是一种 按顺序的集合接口。它最大的特点是 保持元素添加的顺序,并且 可以通过索引(从 0 开始)访问单个元素。
List 作为集合框架的一部分,具备以下特性:
- 允许出现重复元素
- 可以通过指定索引获取、更新、删除元素
- 元素数量可以动态增减(不像数组那样固定大小)
这些特性使得 数据操作更加灵活,在实际工作中被频繁使用。
与数组的区别
在 Java 中,数组(如 int[]、String[])同样可以用来存放多个值,但与 List 相比存在多方面差异。
| Comparison Item | Array | List |
|---|---|---|
| Changing number of elements | Not possible (fixed-size) | Possible (can increase/decrease dynamically) |
| Provided functionality | Minimal operations (indexed access, length retrieval) | Rich methods (add, remove, contains, etc.) |
| Type | Can handle primitive types | Object types only (wrapper classes required) |
| Type safety | Arrays checked at compile time | Can strictly specify type with Generics |
因此,List 是一种更灵活、功能更丰富的集合,在许多场景下比数组更实用。
List 接口及其实现类
在 Java 中使用 List 时,通常使用 List 接口声明变量,再通过具体的实现类创建实例。常见的实现类包括:
- ArrayList:结构类似数组,访问速度快,适合查询和随机访问。
- LinkedList:采用双向链表结构,插入、删除速度快,适用于操作频繁的列表。
- Vector:与 ArrayList 类似,但实现了线程安全,因开销稍大,现已不常使用。
一般情况下,ArrayList 是最常用的实现,除非有特殊需求。后文会根据性能对比帮助你根据实际使用场景选择合适的实现类。
3. List 的基本用法
本节将一步步讲解在 Java 中使用 List 的基本操作,主要以 ArrayList 为例,介绍 List 的代表性操作。
List 的声明与初始化
首先,来看使用 ArrayList 对 List 进行基本声明和初始化的方式。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
}
}
通常的做法是使用 List 接口声明变量,并使用 ArrayList 实例化它。泛型用于指定要存储的类型(这里是 String)。
添加元素 (add)
要向 List 添加元素,请使用 add() 方法。
fruits.add("apple");
fruits.add("banana");
fruits.add("orange");
这会依次向 List 添加三个元素。List 保留添加顺序。
获取元素 (get)
要获取指定索引处的元素,请使用 get(int index)。
System.out.println(fruits.get(0)); // "apple" will be displayed
注意,索引从 0 开始。
更新元素 (set)
要更新某个位置的元素,请使用 set(int index, E element)。
fruits.set(1, "grape"); // The second element "banana" is replaced with "grape"
移除元素 (remove)
您也可以通过特定索引或元素本身移除元素。
fruits.remove(0); // Removes the first element
fruits.remove("orange"); // Removes "orange" (only the first match)
获取 List 大小 (size)
可以使用 size() 方法获取当前元素数量。
System.out.println(fruits.size()); // Returns 2, etc.
检查元素是否存在 (contains)
要检查 List 中是否包含特定元素,请使用 contains()。
if (fruits.contains("grape")) {
System.out.println("grape is present");
}
总结:List 常用基本操作
| Operation | Method Example | Description |
|---|---|---|
| Addition | add("element") | Adds to the end |
| Retrieval | get(index) | References an element |
| Update | set(index, new element) | Changes the element at the specified position |
| Removal | remove(index/element) | Removes the specified element |
| Get Size | size() | Gets the number of elements |
| Check Existence | contains("element") | Checks if a specific element exists |
4. List 操作示例
在本章中,我们将介绍使用 Java 的 List 的实际操作示例。有许多情况需要顺序处理列表中的元素,这里我们将介绍使用 for 循环、增强 for 循环和 Stream API 的代表性方法。
使用 for 循环迭代
最基本的方法是在 for 循环中使用索引检索元素。
List<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("banana");
fruits.add("orange");
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i));
}
这种方法允许使用索引进行 精细控制。例如,当您只想处理偶数索引处的元素时,它很有效。
使用增强 for 循环迭代 (for-each)
如果您想顺序处理所有元素而不关心索引,增强 for 循环很方便。
for (String fruit : fruits) {
System.out.println(fruit);
}
语法简单易读,使其成为 最常用的方法之一。对于简单处理,这就足够了。
使用 Lambda 表达式和 Stream API 迭代
从 Java 8 开始,您也可以使用 Stream API 和 lambda 表达式的语法。
fruits.stream().forEach(fruit -> System.out.println(fruit));
这种表示法的优势是 可以链式连接多个过程。例如,您可以根据特定条件轻松过滤然后打印元素。
fruits.stream()
.filter(fruit -> fruit.contains("a"))
.forEach(System.out::println);
在这个示例中,它只打印包含 “a” 的水果。这 特别推荐给那些想习惯函数式编码的人。
选择合适的方法
| Method | Advantages | Suitable Situations |
|---|---|---|
| Regular for loop | Allows index control | Processing that requires element numbers |
| Enhanced for loop | Simple and easy to read syntax | Simple iteration processing |
| Stream API | Strong for conditional and chained processing | When combining filtering, mapping, and reduction |
5. ArrayList 和 LinkedList 的区别和用法
实现 Java 的 List 接口的代表类是 ArrayList 和 LinkedList。两者都可以以相同的方式用作 List,但它们在内部结构和性能特性上有区别,因此在合适的情况下适当使用它们很重要。
ArrayList 的特性及适用用例
ArrayList 内部使用动态数组(可调整大小的数组)。
主要特性:
.
- 对随机访问(基于索引)非常快
- 在列表末尾添加元素速度快(平均 O(1))
- 在中间插入和删除较慢(O(n))
适用场景:
- 需要频繁搜索(
get())的情况 - 元素数量可以在一定程度上预先预测的情况
- 主要进行读取,元素增删极少的处理
List<String> list = new ArrayList<>();
LinkedList 的特性与适用场景
LinkedList 使用 双向链表结构 实现。
主要特性:
- 添加和删除元素速度快(尤其在头部或尾部)
- 随机访问(
get(index))较慢(O(n)) - 内存消耗略高于 ArrayList
适用场景:
- 元素经常被插入或删除的情况(尤其在头部或中间)
- 需要将其用作队列(Queue)或栈(Stack)时
- 侧重于遍历而不需要索引访问时
List<String> list = new LinkedList<>();
性能对比
下表展示了常用操作的 理论时间复杂度(Big O 表示法)。
| Operation | ArrayList | LinkedList |
|---|---|---|
get(int index) | O(1) | O(n) |
add(E e) (at the end) | O(1) | O(1) |
add(int index, E e) | O(n) | O(n) |
remove(int index) | O(n) | O(n) |
| Iteration | O(n) | O(n) |
* 实际处理时间还会受到数据规模、JVM 优化等因素的影响。

实际使用中的差异化要点
- 如果把数据当作列表并通过索引访问,使用 ArrayList
- 如果在头部或中间的插入/删除频繁,使用 LinkedList
- 对性能敏感的处理,务必进行基准测试并验证
6. List 的高级用法
本节将介绍在 Java 中更便捷地使用 List 的高级技巧。List 不仅可以作为简单的数据集合,还可以通过 排序、洗牌、过滤、转换等 操作实现丰富功能。
对 List 进行排序(Collections.sort)
使用 Collections.sort(),可以 将 List 中的元素按升序排序。元素必须实现 Comparable 接口。
import java.util.*;
List<String> fruits = new ArrayList<>();
fruits.add("banana");
fruits.add("apple");
fruits.add("orange");
Collections.sort(fruits);
System.out.println(fruits); // [apple, banana, orange]
使用 Comparator 自定义排序顺序
fruits.sort(Comparator.reverseOrder()); // Sorts in descending order
对 List 进行洗牌(Collections.shuffle)
使用 Collections.shuffle() 可以随机重新排列元素顺序。
Collections.shuffle(fruits);
System.out.println(fruits); // [banana, orange, apple] (example)
这在需要为游戏准备一副牌或实现随机展示顺序时非常有用。
使用 Stream API 进行过滤(filter)
从 Java 8 起,使用 Stream 可以简洁地编写代码,仅提取符合条件的元素。
List<String> filtered = fruits.stream()
.filter(fruit -> fruit.contains("a"))
.collect(Collectors.toList());
System.out.println(filtered); // [apple, banana, orange] (depending on original content and filter)
使用 Stream API 进行转换(map)
使用 map() 将元素转换为不同的格式。
List<Integer> lengths = fruits.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths); // Lengths of each fruit name [5, 6, 6] etc.
map() 是进行 数据格式转换和预处理 的强大工具。
高级操作汇总
| Operation | Usage Example | Main Use Cases |
|---|---|---|
| Sort | Collections.sort(list) | Sort in ascending order |
| Shuffle | Collections.shuffle(list) | Randomize the order of elements |
| Filter | stream().filter(...).collect() | Extract only elements that match a condition |
| Transform | stream().map(...).collect() | Transform the type or value of elements |
7. 常见错误及解决方案
在 Java 中使用 List 时,初学者经常会碰到 “异常(错误)”。本节将重点说明常见错误的代表案例、产生原因以及解决办法。
IndexOutOfBoundsException
原因:
尝试访问不存在的索引时会抛出此异常。
List<String> list = new ArrayList<>();
list.add("apple");
System.out.println(list.get(1)); // Error: Index 1 out of bounds
解决方案:
检查大小后再访问,或使用条件分支控制访问,以确保索引有效。
if (list.size() > 1) {
System.out.println(list.get(1));
}
空指针异常
原因:
当在 List 或 List 元素为 null 时调用方法会发生此异常。
List<String> list = null;
list.add("apple"); // NullPointerException occurs
解决方案:
事先检查变量是否为 null,或使用 Optional 等方式。
if (list != null) {
list.add("apple");
}
另外,要注意不要忘记初始化:
List<String> list = new ArrayList<>(); // Correct initialization
并发修改异常
原因:
在使用 for-each 循环或 Iterator 遍历 List 时直接修改 List 会导致此异常。
for (String fruit : list) {
if (fruit.equals("banana")) {
list.remove(fruit); // ConcurrentModificationException
}
}
解决方案:
使用 Iterator 安全地删除元素,或使用 removeIf() 等方法。
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("banana")) {
it.remove(); // Safe removal
}
}
或者,从 Java 8 开始可以更简洁地写:
list.removeIf(fruit -> fruit.equals("banana"));
其他需要注意的点
- 检查 List 是否为 null
- 声明变量却未使用是很常见的情况。初始化是必不可少的。
- 了解索引从 0 开始
- 初学者常误以为“第一个元素的索引是 1”。
错误对策汇总
| Error Name | Primary Cause | Example Solutions |
|---|---|---|
| IndexOutOfBoundsException | Accessing a non-existent index | Check length with size() |
| NullPointerException | List or element is null | Don’t forget initialization, perform null checks |
| ConcurrentModificationException | Directly modifying the List during iteration | Operate with Iterator or utilize removeIf() |
8. 结论
回顾 Java List 基础
在本文中,我们一步步解释了 Java 中 List 的基础到高级用法。List 在 Java 集合中使用频率极高,是灵活处理数据的重要工具。
首先,在了解 List 是什么之后,我们学习了以下要点:
- List 是有序且允许重复的集合,并支持索引操作
- 代表性的实现类有 ArrayList 和 LinkedList,它们各有不同的特性和使用场景
- 掌握基本操作(add、get、update、remove、search)即可灵活地操作数据
- 根据情况选择合适的迭代处理,如 for 循环、增强 for 循环和 Stream API
- 支持排序、过滤、转换等高级操作
- 了解常见错误、原因及解决方案有助于防止问题
区分 ArrayList 与 LinkedList 的使用
选择使用哪种 List 实现很重要,应基于处理内容和数据量。以下标准可供参考:
- ArrayList:频繁随机访问,主要用于读取
- LinkedList:频繁插入/删除,访问顺序重要
面向未来的学习
List 只是 Java 集合的“入口”。要处理更高级的数据结构和工具,建议深入了解以下类和特性:
- Set 和 Map:管理唯一元素,键值对结构
- Collections 工具类:排序、查找最小/最大值等
- 使用 Stream API:引入函数式编程
- 了解泛型:类型安全的集合操作
掌握 List 基础将使您的整体 Java 编程更易于管理。
常见问题 (FAQ)
我们汇总了初学者在 Java List 使用中常见的疑问,并挑选了实践中经常遇到的内容。
Q1. Java 的 List 与数组有什么区别?
A. 数组的元素数量是固定的,必须在声明时确定大小。而 List 的大小是可变的,支持灵活的添加和删除元素。此外,List 提供了许多便利的方法(add、remove、contains 等),在可读性和可维护性方面更具优势。
Q2. 应该使用 ArrayList 还是 LinkedList?
A. ArrayList 主要适用于需要频繁随机访问(通过索引检索)的场景。LinkedList 则适合频繁进行元素插入和删除的情况。若不确定,通常建议先使用 ArrayList。
Q3. 我可以在 List 中存储基本类型(如 int 或 double)吗?
A. 不能直接存储。由于 Java 的 List 只处理对象类型,基本类型如 int 需要使用对应的包装类(Integer、Double 等)来存放。
List<Integer> numbers = new ArrayList<>();
numbers.add(10); // Auto-boxed and stored as Integer type
Q4. 如何对 List 中的元素进行排序?
A. 可以使用 Collections.sort(list) 进行升序排序。如果想自定义排序顺序,可以提供一个 Comparator 来实现灵活的排序方式。
Q5. 如果想管理不含重复元素的集合该怎么办?
A. List 本身允许重复元素。若要避免重复,可考虑使用 Set(例如 HashSet),但需要注意集合的顺序不受保证。如果仍想保持 List 结构并去除重复,可以使用以下 Stream 处理方式:
List<String> distinctList = list.stream()
.distinct()
.collect(Collectors.toList());
Q6. 想要清空 List 中的所有元素该怎么做?
A. 可以调用 clear() 方法来移除 List 中的所有元素。
list.clear();
Q7. List 中最常用的操作有哪些?
A. 实际使用中最常用的操作是 add(添加)、get(获取)、remove(删除)和 size(获取大小)。掌握这几项基本能覆盖大多数日常处理需求。

