1. 類別 ArrayList
在 Java 中,介面 List 是一份契約:「我是有序的元素集合,並且可以依據編號(索引)取得元素」。
清單(List)的關鍵特性:
- 元素會以特定順序儲存(不同於 Set)。
- 允許重複元素(例如:可以出現兩次 "Vasya")。
- 可依索引存取元素:第一個元素索引為 0,第二個為 1,以此類推。
- 可在清單的任意位置新增或刪除元素。
介面 List 最常見的實作是 ArrayList。其底層使用一般陣列,並會隨資料成長自動擴充。
如何建立 ArrayList?
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String[] args) {
// 建立字串清單
List<String> students = new ArrayList<>();
}
}
說明
- List<String> 是「字串清單」型別的變數。以介面宣告變數,而以具體類別(ArrayList)建立物件。
- 尖括號 <String> 指定清單中元素的型別(泛型,generics)。
ArrayList(及任何 List)的主要方法:
- add(element) — 將元素加入清單末尾。
- add(index, element) — 在指定索引插入元素。
- get(index) — 依索引取得元素。
- set(index, element) — 依索引替換元素。
- remove(index) — 依索引刪除元素。
- remove(Object) — 刪除第一個與指定物件相等的元素。
- size() — 取得清單的元素數量。
範例:操作學生清單
import java.util.ArrayList;
import java.util.List;
public class StudentListDemo {
public static void main(String[] args) {
List<String> students = new ArrayList<>();
// 新增學生
students.add("Vasya");
students.add("Petya");
students.add("Masha");
// 輸出所有學生
System.out.println("學生清單:" + students);
// 取得第一位學生
String first = students.get(0);
System.out.println("第一位學生:" + first);
// 修改第二位學生的名字
students.set(1, "Pavel");
System.out.println("修改後:" + students);
// 刪除 Masha
students.remove("Masha");
System.out.println("刪除 Masha 之後:" + students);
// 清單大小
System.out.println("學生總數:" + students.size());
}
}
輸出結果:
學生清單:[Vasya, Petya, Masha]
第一位學生:Vasya
修改後:[Vasya, Pavel, Masha]
刪除 Masha 之後:[Vasya, Pavel]
學生總數:2
這和我們的應用程式有何關聯?
假設我們有一個學生任務管理的應用程式。現在可以將任務清單以 List<String> 儲存(而非陣列),並可動態新增新任務。
2. 類別 LinkedList:當插入與刪除速度很重要
LinkedList 是另一種清單實作。其結構是雙向連結串列:每個節點都知道前一個與下一個元素。就像火車車廂:在中間插入一節「車廂」很快,無需重排整列車。
建立 LinkedList
import java.util.LinkedList;
import java.util.List;
public class Example {
public static void main(String[] args) {
List<String> tasks = new LinkedList<>();
}
}
LinkedList 的特性
- 在清單開頭與中間的插入與刪除很快。
- 按索引存取較慢(要找到第 100 個元素,必須沿鏈逐一走訪)。
- 適合經常不只在末尾,還會在開頭或中間新增/刪除元素的情況。
範例:使用 LinkedList
import java.util.LinkedList;
import java.util.List;
public class TaskListDemo {
public static void main(String[] args) {
List<String> tasks = new LinkedList<>();
tasks.add("醒來");
tasks.add("吃早餐");
tasks.add("去上課");
// 在開頭加入任務
tasks.add(0, "設置鬧鐘");
System.out.println("任務清單:" + tasks);
// 刪除第一個任務(最早的)
tasks.remove(0);
System.out.println("刪除第一個任務之後:" + tasks);
}
}
3. ArrayList 與 LinkedList 的比較
| 準則 | |
|
|---|---|---|
| 底層結構 | 陣列 | 雙向連結串列 |
| 按索引的快速存取 | 是 (O(1)) | 否 (O(n)) |
| 在開頭/中間的快速插入/刪除 | 否 (O(n)) | 是 (O(1) — 若已持有節點參考) |
| 在末尾的快速插入/刪除 | 是(通常為 O(1)) | 是 (O(1)) |
| 記憶體 | 占用較少記憶體 | 較多(需額外儲存前後節點的參考) |
| 典型場景 | 經常按索引存取 | 頻繁插入/刪除 |
簡單規則:
- 需要依索引快速存取時 — 使用 ArrayList。
- 經常在開頭或中間新增/刪除時 — 使用 LinkedList。
4. 清單的常見操作
遍歷清單元素
一般 for 迴圈
for (int i = 0; i < students.size(); i++) {
System.out.println("學生 #" + i + ": " + students.get(i));
}
for-each 迴圈(最常用的方法)
for (String name : students) {
System.out.println("姓名:" + name);
}
Lambda 運算式(Java 8+)
students.forEach(name -> System.out.println("姓名:" + name));
查找元素
- contains(element) — 若元素存在於清單中則回傳 true。
- indexOf(element) — 回傳元素第一次出現的索引;若找不到則為 -1。
if (students.contains("Vasya")) {
System.out.println("Vasya 在清單中!");
}
int index = students.indexOf("Vasya");
System.out.println("Vasya 的索引:" + index);
清空清單
clear() — 刪除所有元素。
students.clear();
System.out.println("清空後的清單:" + students);
5. 什麼時候用 ArrayList,什麼時候用 LinkedList?
可以這麼想:ArrayList 適用於需要依索引快速存取,且清單變動不頻繁的情境。例如果很長的使用者清單或訊息歷史——主要以讀取為主。
相反地,LinkedList 適合在開頭/中間經常插入或刪除元素。這可能是佇列、堆疊或復原(撤銷)歷史。
實務上最常用的是 ArrayList。而 LinkedList 比較像是「備用工具」:平時放在工具箱裡,但偶爾剛好派上用場。
6. 操作清單時的常見錯誤
錯誤 1:索引越界。 最常見的情況是以不存在的索引存取元素。若清單只有三個元素,卻呼叫 students.get(5),會拋出 IndexOutOfBoundsException。在存取前請先檢查 size()。
錯誤 2:遍歷時刪除元素。 以 for-each 遍歷並同時刪除元素會導致 ConcurrentModificationException。若要進行複雜的刪除,請使用索引迴圈或 Iterator。
錯誤 3:不正確的物件比較。 若你儲存的是自訂物件(例如 Student),contains 與 remove 會依賴 equals。如果沒有覆寫它,比較將會以參考為準,而非內容。
錯誤 4:使用 raw type。 不要寫 List list = new ArrayList() —— 一定要標明元素型別:List<String> list = new ArrayList<>()。泛型能避免錯誤並讓程式碼更清晰。
GO TO FULL VERSION