Java List集合知识点

一、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实现类选择指南:

  1. ArrayList(最常用)
  • 场景:需要频繁随机访问,元素数量基本固定

  • 优点:随机访问快,内存连续

  • 缺点:插入删除慢(需要移动元素)

  1. LinkedList
  • 场景:需要频繁在头部或中间插入删除

  • 优点:插入删除快,可作为队列/栈使用

  • 缺点:随机访问慢,内存不连续

  1. CopyOnWriteArrayList
  • 场景:读多写少的并发环境

  • 优点:读操作不需要锁,线程安全

  • 缺点:写操作性能差(复制数组),内存占用大

  1. Vector(不推荐)
  • 场景:遗留代码,需要线程安全

  • 缺点:性能较差,已过时

  1. 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));

}

}

总结

核心知识点:

  1. List接口特性:有序、可重复、可通过索引访问

  2. ArrayList vs LinkedList:根据访问模式和操作需求选择

  3. 线程安全:使用CopyOnWriteArrayList或同步包装

  4. Java 8+特性:Stream API、Lambda表达式、方法引用

  5. 性能优化:预分配容量、批量操作、选择合适的实现

最佳实践:

· 大部分情况使用ArrayList

· 需要队列/栈功能时使用LinkedList

· 多线程环境考虑并发集合

· 使用不可变集合提高安全性

· 合理利用Stream API简化代码

相关推荐
superman超哥2 小时前
仓颉元编程之魂:宏系统的设计哲学与深度实践
开发语言·后端·仓颉编程语言·仓颉·仓颉语言·仓颉语言特性
一 乐2 小时前
健身房预约|基于springboot + vue健身房预约小程序系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习·小程序
CC.GG2 小时前
【C++】哈希表的实现
java·c++·散列表
玄同7652 小时前
Python 数据类型:LLM 语料与 API 参数的底层处理逻辑
开发语言·人工智能·python·自然语言处理·llm·nlp·知识图谱
Slow菜鸟2 小时前
Java基础 | 布隆过滤器
java·开发语言
比奇堡派星星2 小时前
Linux Hotplug 机制详解
linux·开发语言·驱动开发
云飞云共享云桌面2 小时前
河北某机器人工厂8个研发设计共享一台SolidWorks云主机
运维·服务器·网络·数据库·算法·性能优化·机器人
元亓亓亓3 小时前
LeetCode热题100--152. 乘积最大子数组--中等
算法·leetcode·职场和发展
molaifeng3 小时前
像搭积木一样理解 Golang AST
开发语言·后端·golang