一、List接口概述
1.1 List接口特点
import java.util.*;
public class ListOverview {
public static void main(String[] args) {
// List接口的特点:
// 1. 有序集合(插入顺序)
// 2. 允许重复元素
// 3. 可以通过索引访问元素
// 4. 可以包含null元素
List<String> list = new ArrayList<>();
// 添加元素(保持顺序)
list.add("Apple");
list.add("Banana");
list.add("Apple"); // 允许重复
list.add(null); // 允许null
list.add("Orange");
System.out.println("List元素: " + list);
System.out.println("元素个数: " + list.size());
System.out.println("是否包含Apple: " + list.contains("Apple"));
System.out.println("索引2的元素: " + list.get(2));
}
}
二、List的三种实现类
2.1 ArrayList(数组列表)
public class ArrayListDemo {
public static void main(String[] args) {
// ArrayList:基于动态数组实现
// 优点:随机访问快(O(1))
// 缺点:插入删除慢(需要移动元素)
List<String> arrayList = new ArrayList<>();
// 1. 基本操作
arrayList.add("Java");
arrayList.add("Python");
arrayList.add("C++");
arrayList.add(1, "JavaScript"); // 在指定位置插入
System.out.println("ArrayList: " + arrayList);
System.out.println("大小: " + arrayList.size());
// 2. 随机访问
System.out.println("索引2: " + arrayList.get(2));
// 3. 修改元素
arrayList.set(0, "Java SE");
// 4. 删除元素
arrayList.remove("Python"); // 按元素删除
arrayList.remove(1); // 按索引删除
// 5. 清空和判空
System.out.println("是否为空: " + arrayList.isEmpty());
arrayList.clear();
// 6. 构造方法
List<Integer> numbers = new ArrayList<>(20); // 指定初始容量
List<String> copyList = new ArrayList<>(arrayList); // 复制构造
// 7. 遍历方式
List<String> languages = Arrays.asList("Java", "Python", "C++", "JavaScript");
// for循环
for (int i = 0; i < languages.size(); i++) {
System.out.print(languages.get(i) + " ");
}
System.out.println();
// 增强for循环
for (String lang : languages) {
System.out.print(lang + " ");
}
System.out.println();
// 迭代器
Iterator<String> iterator = languages.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
// forEach方法
languages.forEach(lang -> System.out.print(lang + " "));
}
}
2.2 LinkedList(链表)
public class LinkedListDemo {
public static void main(String[] args) {
// LinkedList:基于双向链表实现
// 优点:插入删除快(O(1))
// 缺点:随机访问慢(需要遍历)
List<String> linkedList = new LinkedList<>();
// 1. 添加元素
linkedList.add("First");
linkedList.add("Second");
linkedList.addFirst("Head"); // 添加到头部
linkedList.addLast("Tail"); // 添加到尾部
linkedList.add(2, "Middle"); // 在指定位置插入
System.out.println("LinkedList: " + linkedList);
// 2. 获取元素
System.out.println("第一个元素: " + linkedList.getFirst());
System.out.println("最后一个元素: " + linkedList.getLast());
System.out.println("索引2: " + linkedList.get(2));
// 3. 删除元素
linkedList.removeFirst();
linkedList.removeLast();
linkedList.remove("Middle");
// 4. LinkedList特有方法(作为队列使用)
LinkedList<String> queue = new LinkedList<>();
// 队列操作
queue.offer("Task1"); // 入队
queue.offer("Task2");
queue.offer("Task3");
System.out.println("队列: " + queue);
System.out.println("出队: " + queue.poll()); // 出队
System.out.println("查看队首: " + queue.peek());
// 5. LinkedList作为栈使用
LinkedList<String> stack = new LinkedList<>();
// 栈操作
stack.push("Element1"); // 压栈
stack.push("Element2");
stack.push("Element3");
System.out.println("栈: " + stack);
System.out.println("弹栈: " + stack.pop()); // 弹栈
System.out.println("查看栈顶: " + stack.peek());
}
}
2.3 Vector和Stack
public class VectorStackDemo {
public static void main(String[] args) {
// Vector:线程安全的动态数组(已过时,不建议使用)
// Stack:继承自Vector,后进先出(LIFO)
// Vector示例
Vector<String> vector = new Vector<>();
vector.add("Element1");
vector.add("Element2");
vector.addElement("Element3"); // 特有方法
System.out.println("Vector: " + vector);
System.out.println("容量: " + vector.capacity());
System.out.println("大小: " + vector.size());
// Stack示例
Stack<Integer> stack = new Stack<>();
// 压栈
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("Stack: " + stack);
System.out.println("栈顶: " + stack.peek()); // 查看栈顶
// 弹栈
while (!stack.isEmpty()) {
System.out.println("弹出: " + stack.pop());
}
// 更现代的替代方案
Deque<Integer> modernStack = new ArrayDeque<>();
modernStack.push(100);
modernStack.push(200);
System.out.println("Modern stack: " + modernStack.pop());
}
}
三、List的常用操作
3.1 元素操作
public class ListOperations {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 1. 批量添加
list.addAll(Arrays.asList("A", "B", "C", "D", "E"));
// 2. 批量删除
List<String> toRemove = Arrays.asList("B", "D");
list.removeAll(toRemove);
System.out.println("删除B和D后: " + list);
// 3. 保留交集
list.retainAll(Arrays.asList("A", "C", "F"));
System.out.println("保留交集后: " + list);
// 4. 转换为数组
Object[] array1 = list.toArray();
String[] array2 = list.toArray(new String[0]);
// 5. 查找元素位置
list.addAll(Arrays.asList("X", "Y", "X", "Z"));
System.out.println("X第一次出现的位置: " + list.indexOf("X"));
System.out.println("X最后一次出现的位置: " + list.lastIndexOf("X"));
// 6. 子列表
List<String> subList = list.subList(1, 3);
System.out.println("子列表(1-3): " + subList);
// 修改子列表会影响原列表
subList.set(0, "MODIFIED");
System.out.println("原列表: " + list);
}
}
3.2 遍历和修改
public class ListIteration {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list, "A", "B", "C", "D", "E");
System.out.println("原始列表: " + list);
// 1. 使用ListIterator(可以向前向后遍历,可以修改)
ListIterator<String> listIterator = list.listIterator();
// 向前遍历
System.out.print("向前遍历: ");
while (listIterator.hasNext()) {
String element = listIterator.next();
System.out.print(element + " ");
// 修改元素
if ("C".equals(element)) {
listIterator.set("C_MODIFIED");
}
// 添加元素
if ("B".equals(element)) {
listIterator.add("B_NEW");
}
}
System.out.println("\n修改后列表: " + list);
// 向后遍历
System.out.print("向后遍历: ");
while (listIterator.hasPrevious()) {
System.out.print(listIterator.previous() + " ");
}
System.out.println();
// 2. 使用for循环修改元素(可能抛出异常)
try {
for (String s : list) {
if ("A".equals(s)) {
// list.remove(s); // 会抛出ConcurrentModificationException
}
}
} catch (Exception e) {
System.out.println("遍历时修改异常: " + e);
}
// 3. 安全删除方式
List<String> toDelete = new ArrayList<>();
for (String s : list) {
if (s.startsWith("B")) {
toDelete.add(s);
}
}
list.removeAll(toDelete);
System.out.println("安全删除后: " + list);
// 4. 使用removeIf(Java 8+)
list.removeIf(s -> s.contains("MODIFIED"));
System.out.println("使用removeIf后: " + list);
}
}
四、List排序和比较
4.1 自然排序和自定义排序
import java.util.*;
class Student implements Comparable<Student> {
String name;
int score;
Student(String name, int score) {
this.name = name;
this.score = score;
}
// 实现Comparable接口(自然排序)
@Override
public int compareTo(Student other) {
// 按分数降序,分数相同按姓名升序
if (this.score != other.score) {
return other.score - this.score; // 降序
}
return this.name.compareTo(other.name);
}
@Override
public String toString() {
return name + ":" + score;
}
}
public class ListSorting {
public static void main(String[] args) {
// 1. 字符串排序
List<String> names = new ArrayList<>();
names.add("Tom");
names.add("Alice");
names.add("Bob");
names.add("David");
System.out.println("原始: " + names);
// 自然排序(升序)
Collections.sort(names);
System.out.println("升序: " + names);
// 降序排序
Collections.sort(names, Collections.reverseOrder());
System.out.println("降序: " + names);
// 自定义排序(按长度)
names.sort((s1, s2) -> s1.length() - s2.length());
System.out.println("按长度: " + names);
// 2. 对象排序
List<Student> students = new ArrayList<>();
students.add(new Student("Tom", 85));
students.add(new Student("Alice", 92));
students.add(new Student("Bob", 85));
students.add(new Student("David", 78));
System.out.println("\n原始学生: " + students);
// 使用Comparable自然排序
Collections.sort(students);
System.out.println("按分数降序: " + students);
// 使用Comparator自定义排序
students.sort((s1, s2) -> s1.name.compareTo(s2.name));
System.out.println("按姓名升序: " + students);
// 使用Comparator.comparing(Java 8+)
students.sort(Comparator.comparing(Student::getName));
System.out.println("按姓名: " + students);
students.sort(Comparator.comparingInt(Student::getScore).reversed());
System.out.println("按分数降序: " + students);
// 多级排序
students.sort(Comparator
.comparingInt(Student::getScore).reversed()
.thenComparing(Student::getName));
System.out.println("多级排序: " + students);
}
}
4.2 二分查找
public class BinarySearchDemo {
public static void main(String[] args) {
// 二分查找要求列表有序
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 20; i += 2) {
numbers.add(i); // 0, 2, 4, ..., 18
}
System.out.println("有序列表: " + numbers);
// 二分查找(返回索引,找不到返回负数)
int index = Collections.binarySearch(numbers, 10);
System.out.println("查找10的索引: " + index);
index = Collections.binarySearch(numbers, 11);
System.out.println("查找11的索引: " + index); // 负数表示找不到
// 使用自定义比较器查找
List<String> names = Arrays.asList("Alice", "Bob", "David", "Tom");
names.sort(String.CASE_INSENSITIVE_ORDER);
index = Collections.binarySearch(
names,
"bob",
String.CASE_INSENSITIVE_ORDER
);
System.out.println("查找bob(忽略大小写): " + index);
}
}
五、线程安全的List
5.1 同步包装和并发集合
import java.util.*;
import java.util.concurrent.*;
public class ThreadSafeLists {
public static void main(String[] args) throws InterruptedException {
// 1. 使用Collections.synchronizedList(旧方式)
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 遍历时需要手动同步
synchronized(syncList) {
for (String s : syncList) {
System.out.println(s);
}
}
// 2. 使用CopyOnWriteArrayList(读多写少场景)
// 写操作时复制整个数组,读操作不需要锁
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
// 添加元素
cowList.add("Item1");
cowList.add("Item2");
cowList.addIfAbsent("Item1"); // 如果不存在则添加
System.out.println("CopyOnWriteArrayList: " + cowList);
// 遍历时是安全的(遍历的是副本)
for (String item : cowList) {
System.out.println(item);
cowList.add("NewItem"); // 不会影响当前遍历
}
// 3. 性能比较
System.out.println("\n--- 性能测试 ---");
// 测试ArrayList(非线程安全)
List<Integer> arrayList = new ArrayList<>();
testListPerformance(arrayList, "ArrayList");
// 测试同步List
List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());
testListPerformance(synchronizedList, "SynchronizedList");
// 测试CopyOnWriteArrayList
CopyOnWriteArrayList<Integer> copyOnWriteList = new CopyOnWriteArrayList<>();
testListPerformance(copyOnWriteList, "CopyOnWriteArrayList");
}
private static void testListPerformance(List<Integer> list, String name)
throws InterruptedException {
int threads = 10;
int iterations = 1000;
ExecutorService executor = Executors.newFixedThreadPool(threads);
long startTime = System.currentTimeMillis();
for (int i = 0; i < threads; i++) {
executor.submit(() -> {
for (int j = 0; j < iterations; j++) {
list.add(j);
if (list.size() > 0) {
list.get(0);
}
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
long endTime = System.currentTimeMillis();
System.out.printf("%s: %d ms, 大小: %d%n",
name, endTime - startTime, list.size());
}
}
六、Java 8+ 新特性
6.1 Stream API操作List
import java.util.*;
import java.util.stream.*;
public class ListStreamOperations {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 1. 过滤
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("偶数: " + evenNumbers);
// 2. 映射
List<String> strings = numbers.stream()
.map(n -> "Number" + n)
.collect(Collectors.toList());
System.out.println("映射后: " + strings);
// 3. 排序
List<Integer> sortedDesc = numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println("降序: " + sortedDesc);
// 4. 限制和跳过
List<Integer> limited = numbers.stream()
.skip(3) // 跳过前3个
.limit(5) // 限制5个
.collect(Collectors.toList());
System.out.println("跳过3限制5: " + limited);
// 5. 去重
List<Integer> withDuplicates = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> distinct = withDuplicates.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("去重后: " + distinct);
// 6. 统计
long count = numbers.stream().count();
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
Optional<Integer> max = numbers.stream().max(Integer::compare);
Optional<Integer> min = numbers.stream().min(Integer::compare);
double average = numbers.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0);
System.out.printf("统计: 数量=%d, 和=%d, 最大=%s, 最小=%s, 平均=%.2f%n",
count, sum, max.orElse(null), min.orElse(null), average);
// 7. 分组
List<String> words = Arrays.asList("apple", "banana", "cherry",
"date", "elderberry", "fig");
Map<Integer, List<String>> groupedByLength = words.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println("按长度分组: " + groupedByLength);
// 8. 并行流
List<Integer> parallelProcessed = numbers.parallelStream()
.map(n -> n * 2)
.collect(Collectors.toList());
System.out.println("并行处理: " + parallelProcessed);
// 9. 创建不可变List
List<String> immutableList = List.of("A", "B", "C"); // Java 9+
// immutableList.add("D"); // 会抛出UnsupportedOperationException
System.out.println("不可变List: " + immutableList);
// 10. 复制List
List<String> original = new ArrayList<>(Arrays.asList("X", "Y", "Z"));
List<String> copy = List.copyOf(original); // Java 10+
System.out.println("复制List: " + copy);
}
}
七、性能比较和选择策略
7.1 不同List实现的比较
public class ListPerformanceComparison {
public static void main(String[] args) {
int size = 100000;
System.out.println("=== 性能比较(单位:纳秒)===");
// ArrayList
List<Integer> arrayList = new ArrayList<>();
long arrayListAddTime = testAdd(arrayList, size);
long arrayListAccessTime = testRandomAccess(arrayList);
long arrayListInsertTime = testInsert(arrayList, size / 2);
long arrayListRemoveTime = testRemove(arrayList, size / 2);
System.out.println("\nArrayList:");
System.out.printf(" 添加: %d%n", arrayListAddTime);
System.out.printf(" 随机访问: %d%n", arrayListAccessTime);
System.out.printf(" 中间插入: %d%n", arrayListInsertTime);
System.out.printf(" 中间删除: %d%n", arrayListRemoveTime);
// LinkedList
List<Integer> linkedList = new LinkedList<>();
long linkedListAddTime = testAdd(linkedList, size);
long linkedListAccessTime = testRandomAccess(linkedList);
long linkedListInsertTime = testInsert(linkedList, size / 2);
long linkedListRemoveTime = testRemove(linkedList, size / 2);
System.out.println("\nLinkedList:");
System.out.printf(" 添加: %d%n", linkedListAddTime);
System.out.printf(" 随机访问: %d%n", linkedListAccessTime);
System.out.printf(" 中间插入: %d%n", linkedListInsertTime);
System.out.printf(" 中间删除: %d%n", linkedListRemoveTime);
}
private static long testAdd(List<Integer> list, int size) {
long start = System.nanoTime();
for (int i = 0; i < size; i++) {
list.add(i);
}
return System.nanoTime() - start;
}
private static long testRandomAccess(List<Integer> list) {
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
int index = (int) (Math.random() * list.size());
list.get(index);
}
return System.nanoTime() - start;
}
private static long testInsert(List<Integer> list, int position) {
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
list.add(position, i);
}
return System.nanoTime() - start;
}
private static long testRemove(List<Integer> list, int position) {
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
if (position < list.size()) {
list.remove(position);
}
}
return System.nanoTime() - start;
}
}
7.2 选择策略总结
public class ListSelectionGuide {
/*
List实现类选择指南:
- ArrayList(最常用)
-
场景:需要频繁随机访问,元素数量基本固定
-
优点:随机访问快,内存连续
-
缺点:插入删除慢(需要移动元素)
- LinkedList
-
场景:需要频繁在头部或中间插入删除
-
优点:插入删除快,可作为队列/栈使用
-
缺点:随机访问慢,内存不连续
- CopyOnWriteArrayList
-
场景:读多写少的并发环境
-
优点:读操作不需要锁,线程安全
-
缺点:写操作性能差(复制数组),内存占用大
- Vector(不推荐)
-
场景:遗留代码,需要线程安全
-
缺点:性能较差,已过时
- Stack(不推荐)
-
场景:需要栈结构
-
替代:使用ArrayDeque
*/
public static void main(String[] args) {
// 示例:根据不同场景选择List
// 场景1:存储用户列表,需要频繁查找
System.out.println("场景1:用户列表(频繁查找)");
List<String> userList = new ArrayList<>();
// ArrayList的get(index)是O(1)
// 场景2:实现任务队列
System.out.println("场景2:任务队列");
Deque<String> taskQueue = new LinkedList<>(); // 使用Deque接口
// 场景3:多线程读取配置
System.out.println("场景3:多线程读取配置");
List<String> configList = new CopyOnWriteArrayList<>();
// 场景4:需要栈结构
System.out.println("场景4:栈结构");
Deque<Integer> stack = new ArrayDeque<>();
}
}
八、实用工具类
8.1 List工具类示例
import java.util.*;
public class ListUtils {
/**
* 分割List
*/
public static <T> List<List<T>> splitList(List<T> list, int batchSize) {
List<List<T>> batches = new ArrayList<>();
for (int i = 0; i < list.size(); i += batchSize) {
int end = Math.min(list.size(), i + batchSize);
batches.add(new ArrayList<>(list.subList(i, end)));
}
return batches;
}
/**
* 合并多个List
*/
@SafeVarargs
public static <T> List<T> mergeLists(List<T>... lists) {
List<T> result = new ArrayList<>();
for (List<T> list : lists) {
result.addAll(list);
}
return result;
}
/**
* 找出两个List的交集
*/
public static <T> List<T> intersection(List<T> list1, List<T> list2) {
List<T> result = new ArrayList<>(list1);
result.retainAll(list2);
return result;
}
/**
* 找出两个List的差集(list1 - list2)
*/
public static <T> List<T> difference(List<T> list1, List<T> list2) {
List<T> result = new ArrayList<>(list1);
result.removeAll(list2);
return result;
}
/**
* 去重(保持顺序)
*/
public static <T> List<T> distinct(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
/**
* 反转List(非原地)
*/
public static <T> List<T> reverse(List<T> list) {
List<T> result = new ArrayList<>(list);
Collections.reverse(result);
return result;
}
/**
* 测试工具类
*/
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> list2 = Arrays.asList(5, 6, 7, 8, 9, 10, 11, 12, 13);
System.out.println("原始list1: " + list1);
System.out.println("原始list2: " + list2);
System.out.println("分割list1(每3个一组): " + splitList(list1, 3));
System.out.println("交集: " + intersection(list1, list2));
System.out.println("差集(list1-list2): " + difference(list1, list2));
System.out.println("差集(list2-list1): " + difference(list2, list1));
System.out.println("合并: " + mergeLists(list1, list2));
List<Integer> withDuplicates = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
System.out.println("去重: " + distinct(withDuplicates));
System.out.println("反转: " + reverse(list1));
}
}
总结
核心知识点:
-
List接口特性:有序、可重复、可通过索引访问
-
ArrayList vs LinkedList:根据访问模式和操作需求选择
-
线程安全:使用CopyOnWriteArrayList或同步包装
-
Java 8+特性:Stream API、Lambda表达式、方法引用
-
性能优化:预分配容量、批量操作、选择合适的实现
最佳实践:
· 大部分情况使用ArrayList
· 需要队列/栈功能时使用LinkedList
· 多线程环境考虑并发集合
· 使用不可变集合提高安全性
· 合理利用Stream API简化代码