一、List 基礎概念
1.1 什么是 List?
List 就像是一個智能書架:
- 可以按順序存放書籍(元素)
- 每本書都有固定位置(索引)
- 可以隨時添加、取出或重新排列書籍
// 創建一個書架(List)
List<String> bookshelf = new ArrayList<>();
1.2 List 的核心特性
特性 | 說明 | 生活類比 |
---|---|---|
有序性 | 元素按添加順序存儲 | 書架上的書按放入順序排列 |
可重復 | 允許相同元素多次出現 | 同一本書可以有多個副本 |
索引訪問 | 通過位置編號快速獲取元素 | 通過書架編號找書 |
動態擴容 | 自動調整存儲空間 | 智能書架自動擴展 |
1.3 List 家族成員
二、List 基本操作?
2.1 創建 List 的三種方式
// 方式1:標準創建(推薦)
List<String> fruits = new ArrayList<>();// 方式2:快速初始化
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 方式3:不可變列表(Java 9+)
List<String> colors = List.of("紅", "綠", "藍");
2.2 添加元素 - 豐富你的書架
fruits.add("蘋果"); // 末尾添加
fruits.add(0, "香蕉"); // 指定位置插入
fruits.addAll(Arrays.asList("橙子", "葡萄")); // 批量添加System.out.println(fruits);
// 輸出: [香蕉, 蘋果, 橙子, 葡萄]
2.3 訪問元素 - 查找書籍
// 獲取單個元素
String firstFruit = fruits.get(0); // "香蕉"// 檢查元素是否存在
boolean hasApple = fruits.contains("蘋果"); // true// 查找元素位置
int appleIndex = fruits.indexOf("蘋果"); // 1
2.4 修改元素 - 替換書籍
// 替換指定位置的元素
String oldFruit = fruits.set(1, "青蘋果");
System.out.println("被替換的水果: " + oldFruit); // "蘋果"
2.5 刪除元素 - 清理書架
// 按索引刪除
String removed = fruits.remove(0); // 刪除"香蕉"// 按元素值刪除
boolean isRemoved = fruits.remove("葡萄"); // true// 批量刪除
fruits.removeAll(Arrays.asList("橙子", "青蘋果"));// 清空書架
fruits.clear();
三、遍歷 List 的多種方式?
3.1 基礎遍歷方法
// 1. for循環(索引訪問)
for (int i = 0; i < fruits.size(); i++) {System.out.println((i+1) + ". " + fruits.get(i));
}// 2. 增強for循環
for (String fruit : fruits) {System.out.println(fruit);
}
3.2 使用迭代器
// 3. Iterator遍歷
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {String fruit = iterator.next();if (fruit.equals("葡萄")) {iterator.remove(); // 安全刪除}
}
3.3 Java 8+ 高級遍歷
// 4. forEach方法
fruits.forEach(fruit -> System.out.println(fruit));// 5. 方法引用
fruits.forEach(System.out::println);// 6. 并行流遍歷(大數據量)
fruits.parallelStream().forEach(fruit -> {// 并行處理邏輯
});
3.4 遍歷性能對比
遍歷方式 | 10萬元素耗時 | 適用場景 |
---|---|---|
for循環 | 5ms | 需要索引時 |
增強for | 7ms | 簡單遍歷 |
Iterator | 8ms | 需要刪除元素時 |
forEach | 10ms | 函數式編程 |
并行流 | 3ms | 大數據量處理 |
四、List 高級操作?
4.1 排序操作
List<Integer> numbers = Arrays.asList(5, 2, 9, 1, 3);// 自然排序(升序)
Collections.sort(numbers);
// 輸出: [1, 2, 3, 5, 9]// 自定義排序(降序)
numbers.sort((a, b) -> b - a);
// 輸出: [9, 5, 3, 2, 1]// 對象排序
List<Book> books = new ArrayList<>();
books.add(new Book("Java編程", 99));
books.add(new Book("Python入門", 69));books.sort(Comparator.comparing(Book::getPrice));
4.2 過濾與轉換
// 過濾高價水果
List<String> expensiveFruits = fruits.stream().filter(fruit -> fruit.length() > 2).collect(Collectors.toList());// 水果名稱轉大寫
List<String> upperCaseFruits = fruits.stream().map(String::toUpperCase).collect(Collectors.toList());
4.3 數學統計
IntSummaryStatistics stats = numbers.stream().mapToInt(Integer::intValue).summaryStatistics();System.out.println("最大值: " + stats.getMax());
System.out.println("最小值: " + stats.getMin());
System.out.println("平均值: " + stats.getAverage());
System.out.println("總數: " + stats.getSum());
4.4 列表轉換
// List轉數組
String[] fruitArray = fruits.toArray(new String[0]);// 數組轉List(不可修改)
List<String> fixedList = Arrays.asList(fruitArray);// 數組轉List(可修改)
List<String> modifiableList = new ArrayList<>(Arrays.asList(fruitArray));
五、ArrayList 深度解析?
5.1 內部結構
ArrayList 就像是一個智能伸縮書架:
// 簡化版ArrayList實現
public class SimpleArrayList<E> {private Object[] elements; // 存儲元素的數組private int size; // 當前元素數量public SimpleArrayList() {this.elements = new Object[10]; // 初始容量}public void add(E element) {// 當數組滿時自動擴容if (size == elements.length) {Object[] newArray = new Object[elements.length * 2];System.arraycopy(elements, 0, newArray, 0, size);elements = newArray;}elements[size++] = element;}
}
5.2 擴容機制
5.3 性能特點
操作 | 時間復雜度 | 說明 |
---|---|---|
get(index) | O(1) | 直接通過索引訪問 |
add(element) | O(1) | 平均時間復雜度 |
add(index, element) | O(n) | 需要移動后續元素 |
remove(index) | O(n) | 需要移動后續元素 |
contains(element) | O(n) | 需要遍歷查找 |
六、LinkedList 深度解析
6.1 內部結構
LinkedList 就像是一個帶前后指針的書本鏈:
// 簡化版鏈表節點
class Node<E> {E item; // 當前元素Node<E> next; // 下一個節點Node<E> prev; // 上一個節點Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}
}
6.2 操作原理
// 在指定位置插入元素
public void add(int index, E element) {// 1. 找到目標位置的節點Node<E> target = getNode(index);// 2. 創建新節點Node<E> newNode = new Node<>(target.prev, element, target);// 3. 調整前后節點指針target.prev.next = newNode;target.prev = newNode;
}
6.3 性能對比 ArrayList
操作 | ArrayList | LinkedList | 適用場景 |
---|---|---|---|
隨機訪問 | ?? 快 (O(1)) | 🐢 慢 (O(n)) | 需要頻繁按索引訪問 |
頭部插入 | 🐢 慢 (O(n)) | ?? 快 (O(1)) | 需要頻繁在開頭添加 |
尾部插入 | ?? 快 (O(1)) | ?? 快 (O(1)) | 在末尾添加 |
中間插入 | 🐢 慢 (O(n)) | ?? 快 (O(1)) | 需要頻繁在中間插入 |
內存占用 | 較少(僅需存儲元素) | 較多(每個元素需要額外指針) | 內存敏感場景 |
七、實戰應用案例
7.1 學生成績管理系統
class Student {private String name;private int score;// 構造方法、getter/setter省略
}public class GradeSystem {private List<Student> students = new ArrayList<>();// 添加學生public void addStudent(Student student) {students.add(student);}// 按分數排序public void sortByScore() {students.sort(Comparator.comparingInt(Student::getScore).reversed());}// 查找前N名學生public List<Student> getTopStudents(int n) {return students.stream().sorted(Comparator.comparingInt(Student::getScore).reversed()).limit(n).collect(Collectors.toList());}// 統計分數分布public Map<String, Long> getScoreDistribution() {return students.stream().collect(Collectors.groupingBy(s -> {int score = s.getScore();if (score >= 90) return "優秀";if (score >= 80) return "良好";if (score >= 60) return "及格";return "不及格";},Collectors.counting()));}
}
7.2 購物車實現
class CartItem {private String productId;private String name;private double price;private int quantity;// 構造方法、getter/setter省略
}public class ShoppingCart {private List<CartItem> items = new LinkedList<>();// 添加商品public void addItem(CartItem newItem) {// 檢查是否已存在for (CartItem item : items) {if (item.getProductId().equals(newItem.getProductId())) {item.setQuantity(item.getQuantity() + newItem.getQuantity());return;}}items.add(newItem);}// 更新數量public void updateQuantity(String productId, int newQuantity) {items.removeIf(item -> item.getProductId().equals(productId));if (newQuantity > 0) {items.add(new CartItem(productId, newQuantity));}}// 計算總價public double calculateTotal() {return items.stream().mapToDouble(item -> item.getPrice() * item.getQuantity()).sum();}// 生成訂單public Order checkout() {Order order = new Order();order.setItems(new ArrayList<>(items));order.setTotal(calculateTotal());items.clear();return order;}
}
八、最佳實踐與性能優化
8.1 選擇正確的 List 實現
場景 | 推薦實現 | 理由 |
---|---|---|
讀多寫少 | ArrayList | 隨機訪問快 |
頻繁增刪 | LinkedList | 插入刪除快 |
多線程環境 | CopyOnWriteArrayList | 線程安全 |
固定大小列表 | Arrays.asList() | 內存優化 |
不可變列表 | List.of() | 安全簡潔 |
8.2 性能優化技巧
? ?預分配容量(減少擴容開銷)
// 預計存儲1000個元素
List<String> largeList = new ArrayList<>(1000);
? ? ? ?批量操作(減少方法調用)?
// 差: 多次調用add
for (String item : items) {list.add(item);
}// 好: 批量添加
list.addAll(items);
? ?
避免在循環中調用size
// 差: 每次循環都調用size()
for (int i = 0; i < list.size(); i++) {// ...
}// 好: 緩存size值
int size = list.size();
for (int i = 0; i < size; i++) {// ...
}
使用subList視圖
// 創建子列表視圖(不復制數據)
List<String> sub = list.subList(0, 5);
九、常見問題與解決方案 (30分鐘)
?ConcurrentModificationException
問題:遍歷時修改集合
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String s : list) {if ("B".equals(s)) {list.remove(s); // 拋出異常}
}
解決方案:
// 1. 使用Iterator的remove方法
Iterator<String> it = list.iterator();
while (it.hasNext()) {String s = it.next();if ("B".equals(s)) {it.remove(); // 安全刪除}
}// 2. 使用Java 8+ removeIf
list.removeIf("B"::equals);// 3. 創建副本遍歷
new ArrayList<>(list).forEach(s -> {if ("B".equals(s)) {list.remove(s);}
});
性能陷阱:LinkedList的隨機訪問
問題:
LinkedList<Integer> list = new LinkedList<>();
// 填充數據...// 隨機訪問性能差
for (int i = 0; i < list.size(); i++) {Integer value = list.get(i); // O(n)操作
}
解決方案
// 1. 使用迭代器
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {Integer value = it.next();
}// 2. 使用增強for循環
for (Integer value : list) {// ...
}// 3. 轉換為ArrayList(只讀場景)
List<Integer> arrayList = new ArrayList<>(list);
?對象相等性判斷
問題:自定義對象在List中的行為
解決方案:
class Person {String name;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name);}
}