# Java List完全指南:从入门到高阶应用

发布于:2025-05-10 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、List接口:有序集合的核心

作为Java集合框架中最常用的接口之一,List代表了有序可重复的元素序列。与Set的无序唯一特性形成鲜明对比,List更像是我们日常生活中的排队场景——先来后到很重要,且允许相同元素存在。

1.1 List核心特性

  • 有序性:元素按插入顺序存储(非排序顺序)
  • 可重复:允许存储相同元素的多个实例
  • 索引访问:支持基于下标的高效随机访问
  • 丰富API:提供位置相关的操作方法

1.2 List主要实现类对比

实现类 数据结构 线程安全 随机访问效率 增删效率 适用场景
ArrayList 动态数组 不安全 O(1) O(n) 查询多、增删少的场景
LinkedList 双向链表 不安全 O(n) O(1) 频繁插入删除的场景
Vector 动态数组 安全 O(1) O(n) 需要线程安全的场景(已逐渐被淘汰)
CopyOnWriteArrayList 动态数组 安全 O(1) O(n) 读多写少的并发场景

二、基础操作实战

2.1 创建与初始化

// 最常用的初始化方式(Java 7+)
List<String> arrayList = new ArrayList<>(); 

// 使用Arrays工具类快速初始化
List<Integer> numbers = Arrays.asList(1, 2, 3); 

// Java 9+ 的工厂方法
List<String> immutableList = List.of("A", "B", "C");

// 指定初始容量(优化技巧)
List<Object> optimizedList = new ArrayList<>(100);

2.2 增删改查四连击

List<String> fruits = new ArrayList<>();

// 增(尾部添加)
fruits.add("Apple");  
fruits.add(0, "Banana"); // 指定位置插入

// 删
fruits.remove("Apple");  // 按元素删除
fruits.remove(0);        // 按索引删除

// 改
fruits.set(0, "Mango");  // 替换指定位置元素

// 查
String first = fruits.get(0); // 获取元素
int index = fruits.indexOf("Mango"); // 查找位置
boolean exists = fruits.contains("Apple"); // 存在判断

2.3 遍历方式大比拼

// 1. 经典for循环(适合需要索引的场景)
for (int i = 0; i < fruits.size(); i++) {
    System.out.println(fruits.get(i));
}

// 2. 增强for循环(简洁明了)
for (String fruit : fruits) {
    System.out.println(fruit);
}

// 3. 迭代器(可安全删除元素)
Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
    String fruit = it.next();
    if (fruit.equals("Mango")) {
        it.remove(); // 安全删除
    }
}

// 4. Java 8+ forEach + Lambda
fruits.forEach(fruit -> System.out.println(fruit));

三、进阶技巧与最佳实践

3.1 容量优化之道

ArrayList内部使用Object数组存储数据,默认初始容量为10。当元素数量达到当前容量时,会自动进行1.5倍扩容(int newCapacity = oldCapacity + (oldCapacity >> 1)),这个扩容操作会导致数组复制,影响性能。

优化建议

// 预估数据量较大时指定初始容量
List<BigData> bigList = new ArrayList<>(10000);

// 已知最终大小时使用一次性扩容
ArrayList<String> optimized = new ArrayList<>();
optimized.ensureCapacity(5000); // 预先扩容

3.2 不可变列表的多种实现

// 1. Java 9+的不可变列表
List<String> immutable1 = List.of("A", "B", "C");

// 2. Collections工具类
List<String> immutable2 = Collections.unmodifiableList(mutableList);

// 3. Guava库的不可变列表
ImmutableList<String> immutable3 = ImmutableList.of("A", "B", "C");

// 尝试修改将抛出UnsupportedOperationException
immutable1.add("D"); // 抛出异常

3.3 高效列表合并技巧

List<String> list1 = Arrays.asList("A", "B");
List<String> list2 = Arrays.asList("C", "D");

// 方法1:使用addAll(会修改原集合)
List<String> merged = new ArrayList<>(list1);
merged.addAll(list2);

// 方法2:Java 8 Stream API(不修改原集合)
List<String> merged2 = Stream.concat(list1.stream(), list2.stream())
                            .collect(Collectors.toList());

// 方法3:Guava库的Iterables.concat
Iterable<String> merged3 = Iterables.concat(list1, list2);

四、性能陷阱与规避方案

4.1 随机访问的坑

LinkedList<String> linkedList = new LinkedList<>();
// 填充10000个元素...

// 反模式:链表随机访问(效率O(n))
for (int i = 0; i < linkedList.size(); i++) {
    String item = linkedList.get(i); // 性能灾难!
}

// 正确做法:使用迭代器
for (Iterator<String> it = linkedList.iterator(); it.hasNext(); ) {
    String item = it.next(); // 效率O(1)
}

4.2 并发修改异常

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));

// 错误示例:遍历时修改
for (String s : list) {
    if (s.equals("B")) {
        list.remove(s); // 抛出ConcurrentModificationException
    }
}

// 解决方案1:使用迭代器的remove方法
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if (it.next().equals("B")) {
        it.remove(); // 安全删除
    }
}

// 解决方案2:Java 8+ removeIf
list.removeIf(s -> s.equals("B"));

五、实际应用案例

5.1 分页查询实现

public static <T> List<T> getPage(List<T> sourceList, int page, int pageSize) {
    if (sourceList == null || sourceList.isEmpty()) {
        return Collections.emptyList();
    }
    
    int totalItems = sourceList.size();
    int fromIndex = (page - 1) * pageSize;
    if (fromIndex >= totalItems) {
        return Collections.emptyList();
    }
    
    int toIndex = Math.min(fromIndex + pageSize, totalItems);
    return sourceList.subList(fromIndex, toIndex);
}

// 使用示例
List<Integer> numbers = IntStream.range(0, 100)
                                .boxed()
                                .collect(Collectors.toList());
                                
List<Integer> page3 = getPage(numbers, 3, 10); // 获取第3页数据

5.2 列表排序大全

List<String> names = Arrays.asList("John", "Alice", "Bob");

// 1. 自然排序
Collections.sort(names); // 原地修改
List<String> sorted = names.stream().sorted().toList(); // Java 16+

// 2. 自定义比较器
names.sort(Comparator.comparing(String::length)); // 按长度排序

// 3. 多条件排序
List<Person> people = getPeople();
people.sort(Comparator.comparing(Person::getLastName)
                     .thenComparing(Person::getFirstName));

// 4. 逆序排列
names.sort(Comparator.reverseOrder());

六、与数组的转换技巧

// List → 数组
List<String> list = Arrays.asList("A", "B", "C");
String[] array1 = list.toArray(new String[0]); // 推荐方式
String[] array2 = list.toArray(String[]::new); // Java 11+

// 数组 → List
String[] arr = {"X", "Y", "Z"};
List<String> fromArray1 = Arrays.asList(arr); // 固定大小列表
List<String> fromArray2 = new ArrayList<>(Arrays.asList(arr)); // 可变列表
List<String> fromArray3 = Stream.of(arr).collect(Collectors.toList()); // Java 8

总结与选择建议

  1. 默认选择:大多数情况下优先使用ArrayList,除非有频繁的插入删除操作
  2. 线程安全:考虑Collections.synchronizedListCopyOnWriteArrayList
  3. 性能敏感:预估大小并设置初始容量,避免频繁扩容
  4. 不可变需求:使用List.of()或不可变集合库
  5. 复杂操作:善用Stream API进行过滤/映射/归约等操作

记住:没有最好的实现,只有最适合场景的实现。理解每种List实现的内在机制,才能在实际开发中做出最优选择。


网站公告

今日签到

点亮在社区的每一天
去签到