List是一个接口,它定义了一个列表应该具备的行为规范(add(),get(),size()等)。ArrayList是一个具体的类,它实现了List接口,并提供了一种基于动态数组的数据结构来存储数据。
详细对比
为了更清晰地理解,我们从几个维度进行详细对比。

| 特性 | List |
ArrayList |
|---|---|---|
| 本质 | 接口 | 类 |
| 定义 | java.util.List |
java.util.ArrayList |
| 作用 | 定义一个有序的、允许重复元素的集合的蓝图或契约。 | 是 List 接口最常用、最标准的实现类之一。 |
| 实例化 | 不能被直接实例化(不能 new List())。 |
可以被直接实例化(new ArrayList())。 |
| 多态 | 支持,可以声明为 List 类型,但实例化为 ArrayList 或其他实现类。 |
支持,可以被声明为 List 接口类型,也可以声明为其本身的 ArrayList 类型。 |
| 数据结构 | 不涉及,它只定义行为。 | 动态数组,内部使用一个数组来存储元素,当容量不足时,会自动创建一个更大的数组并复制旧数组的内容。 |
| 性能特点 | 不涉及,取决于其具体的实现类。 | 随机访问快:get(int index) 时间复杂度为 O(1)。尾部添加/删除较快: add(E e) 和 remove(size()-1) 均摊时间复杂度为 O(1)。中间插入/删除慢: add(int index, E element) 和 remove(int index) 时间复杂度为 O(n),因为需要移动大量元素。 |
是否允许 null |
取决于实现类。 | 允许,可以存储多个 null 值。 |
| 是否允许重复 | 允许。 | 允许。 |
核心关系:接口与实现
List 和 ArrayList 的关系是 Java 中 “接口-实现” 经典模式的体现。
// List 是一个接口,它像一个“菜单”,列出了所有可以做的事情
public interface List<E> extends Collection<E> {
// ... 方法列表: add, get, remove, size, iterator 等 ...
}
// ArrayList 是一个类,它实现了这个“菜单”,并且提供了具体的做法
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// ... 内部实现细节 ...
}
这就好比:
List是“汽车”这个概念,它规定了汽车应该有轮子、能载人、能行驶。ArrayList是“丰田卡罗拉”这款具体的汽车,它实现了“汽车”的所有功能,并且有自己特定的发动机和底盘。
代码示例:如何使用?
在实际开发中,我们强烈推荐使用接口来声明,用实现类来实例化,这样可以提高代码的灵活性和可维护性。
推荐的方式(面向接口编程)
import java.util.ArrayList;
import java.util.List;
public class RecommendedWay {
public static void main(String[] args) {
// 声明为 List 接口类型,但实例化为 ArrayList
// 这使得未来如果需要换成 LinkedList,只需修改这一行,其他代码无需改动
List<String> names = new ArrayList<>();
// 添加元素
names.add("Alice");
names.add("Bob");
names.add("Charlie");
// 获取元素
String firstPerson = names.get(0); // "Alice"
System.out.println("First person: " + firstPerson);
// 获取大小
System.out.println("List size: " + names.size()); // 3
// 遍历
for (String name : names) {
System.out.println(name);
}
}
}
不推荐的方式(面向具体类编程)
这种方式虽然也能工作,但会降低代码的灵活性。

import java.util.ArrayList;
public class NotRecommendedWay {
public static void main(String[] args) {
// 直接声明和实例化为 ArrayList
ArrayList<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// 如果将来因为性能需求(比如频繁在头部增删)需要换成 LinkedList,
// 你需要修改所有声明为 `ArrayList` 的地方,非常繁琐。
// List<String> names = new LinkedList<>(); // 需要改动很多地方
}
}
何时选择 ArrayList?
ArrayList 是最通用的列表实现,但在以下场景下它表现出色:
- 需要频繁进行随机访问:如果你经常需要通过索引(
list.get(i))来获取元素,ArrayList是最佳选择,因为它的时间复杂度是 O(1)。 - 主要在列表尾部进行增删操作:如果你只是简单地往列表末尾添加元素或从末尾删除元素,
ArrayList的性能也很好(均摊 O(1))。 - 数据量相对固定,或不需要频繁在中间插入/删除:如果你不需要在列表的中间位置频繁地插入或删除元素,
ArrayList的性能是可以接受的。
何时选择其他 List 实现?
虽然问题只问了 ArrayList,但了解它的“兄弟”有助于你做出更好的选择:
-
LinkedList:- 数据结构:双向链表。
- 优点:在中间或头部进行增删操作非常快(O(1)),因为它只需要修改前后节点的指针即可。
- 缺点:随机访问很慢(O(n)),因为它必须从头或尾开始遍历。
- 适用场景:当你需要频繁在列表的任何位置(尤其是头部)进行增删操作,且不关心随机访问性能时。
-
Vector:
(图片来源网络,侵删)- 数据结构:也是动态数组,与
ArrayList类似。 - 关键区别:
Vector是线程安全的,它的所有方法都带有synchronized关键字。 - 缺点:因为同步开销,性能比
ArrayList差。 - 适用场景:在现代 Java 开发中,很少直接使用
Vector,如果需要线程安全的列表,通常使用Collections.synchronizedList(new ArrayList<>())或者CopyOnWriteArrayList。
- 数据结构:也是动态数组,与
List |
ArrayList |
|
|---|---|---|
| 角色 | 接口 (Interface) | 实现类 (Implementation) |
| 好比 | 菜单、蓝图 | 一道具体的菜、一辆具体的汽车 |
| 使用 | 用于声明变量,定义类型 | 用于实例化对象,创建实例 |
| 核心原则 | 定义“做什么” | 定义“怎么做” |
记住这个黄金法则:
永远面向接口编程(Use the interface as the type)。
也就是说,在你的方法参数、返回值和变量声明中,尽可能地使用
List,而不是ArrayList,只有在创建对象实例时,才指定具体的实现类,如new ArrayList<>(),这是编写高质量、可扩展 Java 代码的关键实践。
