Java中的List接口是Collection框架中最基础也最常用的数据结构之一。作为有序集合的代表,List在日常开发中几乎无处不在。我们先从底层设计角度理解它的核心特性:
List的本质是一个有序序列(ordered sequence),这个"有序"体现在两个方面:
这种设计带来了几个重要特性:
java复制// 典型List使用示例
List<String> languages = new ArrayList<>();
languages.add("Java"); // 索引0
languages.add("Python"); // 索引1
languages.add(null); // 允许null
languages.add("Java"); // 允许重复
System.out.println(languages.get(1)); // 输出"Python"
注意:虽然所有List实现都遵循接口规范,但不同实现类在允许null元素方面有差异。比如ConcurrentLinkedDeque就不允许null,使用时需查阅具体实现类的文档。
ArrayList是最常用的List实现,其底层是动态扩容的Object数组。理解它的扩容机制对性能优化至关重要:
java复制// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 扩容逻辑(简化版)
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
关键性能特点:
实战技巧:如果能预估数据量,创建ArrayList时指定初始容量可避免多次扩容。比如已知要存储1000个元素:
new ArrayList<>(1000)
LinkedList采用经典的双向链表结构,每个节点包含前驱、后继引用:
java复制private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
// 构造方法...
}
其性能特点与ArrayList形成鲜明对比:
虽然现代Java开发中很少直接使用Vector,但理解它的设计仍有价值:
java复制// 现代替代方案
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
java复制// 基本操作示例
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
// 随机访问
String element = list.get(1); // "B"
// 修改元素
list.set(1, "BB"); // ["A", "BB", "C"]
// 检查包含
boolean contains = list.contains("BB"); // true
性能提示:ArrayList的contains()方法是O(n)操作,频繁包含检查应考虑使用HashSet
java复制// 添加元素
list.add("D"); // 尾部添加
list.add(1, "A1"); // 指定位置插入
// 删除元素
list.remove(0); // 按索引删除
list.remove("A1"); // 按元素删除(首次出现)
不同实现的性能差异:
| 操作 | ArrayList | LinkedList |
|---|---|---|
| add(E e) | O(1) 摊销 | O(1) |
| add(int index, E e) | O(n) | O(n) |
| remove(int index) | O(n) | O(n) |
| remove(Object o) | O(n) | O(n) |
java复制// 批量操作
List<String> anotherList = Arrays.asList("X", "Y", "Z");
list.addAll(anotherList); // 并集
list.retainAll(anotherList); // 交集
list.removeAll(anotherList); // 差集
// 子列表(视图)
List<String> subList = list.subList(1, 3);
subList.set(0, "NEW"); // 原list也会被修改
重要注意:subList()返回的是视图而非独立列表,对子列表的修改会影响原列表
java复制// 1. for循环(适合ArrayList)
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 2. 增强for循环(所有实现通用)
for (String item : list) {
System.out.println(item);
}
// 3. 迭代器(可配合remove操作)
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if (shouldRemove(item)) {
it.remove(); // 安全删除
}
}
java复制ListIterator<String> lit = list.listIterator(list.size());
while (lit.hasPrevious()) {
String item = lit.previous();
System.out.println(item);
}
java复制// 过滤与转换
list.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.forEach(System.out::println);
// 并行处理(数据量大时)
list.parallelStream()
.forEach(this::processItem);
在没有泛型的时代(Java5之前),集合使用存在两大痛点:
java复制// 前泛型时代(Java1.4)
List rawList = new ArrayList();
rawList.add("String");
rawList.add(Integer.valueOf(1)); // 编译通过
String s = (String)rawList.get(1); // 运行时ClassCastException
泛型通过类型参数解决了这些问题:
java复制// 集合泛型
List<String> strList = new ArrayList<>();
// 自定义泛型类
class Box<T> {
private T content;
public void set(T content) { this.content = content; }
public T get() { return content; }
}
// 使用
Box<Integer> intBox = new Box<>();
intBox.set(42);
int value = intBox.get();
java复制// 基本泛型方法
public <T> void printArray(T[] array) {
for (T item : array) {
System.out.println(item);
}
}
// 受限类型参数
public <T extends Number> double sum(List<T> numbers) {
double total = 0;
for (T num : numbers) {
total += num.doubleValue();
}
return total;
}
java复制// 1. 无界通配符
public void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
// 2. 上界通配符
public double sumOfList(List<? extends Number> list) {
double sum = 0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
// 3. 下界通配符
public void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
PECS原则(Producer-Extends, Consumer-Super):
- 当只需要从集合读取时,使用
? extends T- 当只需要向集合写入时,使用
? super T- 既要读又要写时,不要使用通配符
Java泛型是通过类型擦除实现的,这是理解泛型限制的关键:
java复制// 编译前
List<String> strList = new ArrayList<>();
strList.add("hello");
String s = strList.get(0);
// 编译后(类型擦除)
List strList = new ArrayList();
strList.add("hello");
String s = (String)strList.get(0); // 自动插入类型转换
桥接方法示例:
java复制// 编译前
class Node<T> {
public T data;
public void setData(T data) { this.data = data; }
}
class MyNode extends Node<Integer> {
public void setData(Integer data) { super.setData(data); }
}
// 编译后会生成桥接方法
class MyNode extends Node {
public void setData(Integer data) { super.setData(data); }
// 桥接方法
public void setData(Object data) {
setData((Integer)data);
}
}
| 选择标准 | 推荐实现 |
|---|---|
| 频繁随机访问 | ArrayList |
| 频繁头尾操作 | LinkedList |
| 线程安全需求 | CopyOnWriteArrayList(读多写少) Collections.synchronizedList |
| 元素唯一性要求 | HashSet/LinkedHashSet |
java复制// 反例:频繁装箱拆箱
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
list.add(i); // 自动装箱发生在这里
}
// 优化:使用原始类型数组(如int[])处理纯数值计算
java复制// 反例:用LinkedList做随机访问
for (int i = 0; i < list.size(); i++) {
String item = list.get(i); // 每次get都是O(n)操作
}
// 正确:使用迭代器
for (String item : list) {
// ...
}
java复制List<String> threadSafeList = new CopyOnWriteArrayList<>();
// 适合配置信息等读多写少的场景
java复制List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 必须手动同步迭代操作
synchronized(syncList) {
for (String item : syncList) {
// ...
}
}
java复制public final class ImmutableList<E> implements List<E> {
private final Object[] elements;
public ImmutableList(Collection<? extends E> c) {
this.elements = c.toArray();
}
@Override
public E get(int index) {
@SuppressWarnings("unchecked")
E result = (E)elements[index];
return result;
}
// 其他方法抛出UnsupportedOperationException
@Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}
}
java复制public class CompositeList<E> implements List<E> {
private final List<E> primary;
private final List<E> secondary;
public CompositeList(List<E> primary, List<E> secondary) {
this.primary = Objects.requireNonNull(primary);
this.secondary = Objects.requireNonNull(secondary);
}
@Override
public int size() {
return primary.size() + secondary.size();
}
@Override
public E get(int index) {
if (index < primary.size()) {
return primary.get(index);
}
return secondary.get(index - primary.size());
}
// 其他方法实现类似逻辑...
}
java复制List<String> list = Arrays.asList("a", "b", "c");
// forEach
list.forEach(System.out::println);
// removeIf
list.removeIf(s -> s.startsWith("a"));
// replaceAll
list.replaceAll(String::toUpperCase);
// sort
list.sort(Comparator.reverseOrder());
java复制// 不可变列表创建
List<String> immutableList = List.of("a", "b", "c");
// 特点:
// 1. 不可变
// 2. 不允许null元素
// 3. 空间优化(可能返回专用实现)
java复制// 更简洁的流收集方式
List<String> filtered = list.stream()
.filter(s -> s.length() > 2)
.toList(); // 返回不可变列表
典型场景:
java复制List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
// 错误写法:在迭代中修改
for (String s : list) {
if (s.equals("b")) {
list.remove(s); // 抛出异常
}
}
// 正确写法1:使用迭代器的remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) {
it.remove(); // 安全删除
}
}
// 正确写法2:Java8+的removeIf
list.removeIf(s -> s.equals("b"));
java复制// 运行时类型检查失效
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
System.out.println(strList.getClass() == intList.getClass()); // 输出true
// 解决方法:使用Class对象传递类型信息
public <T> void checkType(List<T> list, Class<T> type) {
// 可进行运行时类型检查
}
java复制@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class ListBenchmark {
@Benchmark
public void testArrayListAdd(Blackhole bh) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
bh.consume(list);
}
}
bash复制# 启动记录
java -XX:StartFlightRecording=duration=60s,filename=list.jfr ...
# 使用JMC分析记录文件
List的迭代器实现是迭代器模式的经典案例:
java复制public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
// ArrayList中的实现
private class Itr implements Iterator<E> {
int cursor; // 下一个元素的索引
int lastRet = -1; // 上一个返回的元素的索引
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
}
Arrays.asList()是适配器模式的典型实现:
java复制public static <T> List<T> asList(T... a) {
return new ArrayList<>(a); // 注意这个ArrayList是Arrays的内部类
}
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable {
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public E get(int index) {
return a[index];
}
@Override
public int size() {
return a.length;
}
}
32位JVM上一个ArrayList的内存占用估算:
总内存 ≈ 40 + 4N 字节
每个Node节点包含:
总内存 ≈ 24 × N 字节(比ArrayList多约6倍)
java复制// 使用FastUtil的IntArrayList
IntList list = new IntArrayList();
list.add(1);
list.add(2);
int sum = list.stream().sum();
java复制// 自定义序列化可优化ArrayList的存储
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// 只写入size和实际元素
s.defaultWriteObject();
s.writeInt(size);
for (int i = 0; i < size; i++) {
s.writeObject(elementData[i]);
}
}