1. 介紹
我們之前已經了解過「無限長的陣列」——也就是清單。現在讓我們更深入一些,先再問一次:什麼是「傳統」陣列?仔細一看,它像是一個有固定隔間數量的工具箱。假設箱子有 10 個格子。若你出現了第 11 個工具,怎麼辦?只好再買一個新箱子!傳統陣列的大小是固定的,建立之後長度就不會改變。
如果需要新增或刪除元素,就得建立新陣列並手動複製資料。陣列能夠快速透過索引找到元素,但要在中間插入元素並不是單純「插入」,而是「把右邊全部往右移」;刪除時則要「往左移」。這既慢又不方便。除此之外,陣列本身不包含任何額外的「邏輯」:它只是保存一組格子;排序、依內容搜尋或檢查唯一性都得靠外部手段完成。
範例:動態的學生清單
假設你在寫一個用於管理某個班級學生的應用程式。起初有 5 個人,接著又來了一位,然後有人離開。用陣列會是這樣:
String[] students = new String[5];
students[0] = "伊凡";
students[1] = "瑪麗亞";
// 以此類推...
// 糟了,又來了一位學生
// 只好建立新的陣列!
String[] newStudents = new String[6];
for (int i = 0; i < students.length; i++) {
newStudents[i] = students[i];
}
newStudents[5] = "阿列克謝";
students = newStudents;
方便嗎?委婉地說,並不。若這類操作很多呢?你就會想要更方便的工具……
2. 什麼是集合?
集合是用來存放一組其他物件(元素)的容器。集合允許新增、刪除、遍歷元素,並執行其他操作:搜尋、排序、過濾等等。
在 Java 中,所有集合都會實作或繼承介面 Collection(而對映則是 Map)。集合不只是「一堆東西」,而是一個提供便利且設計良好方法的結構,用於操作元素。
為什麼集合是物件?
因為集合是以類別實作,所以你可以建立任何物件的集合、將它們組合、繼承、擴充,並在自己的類別與方法中使用。
範例:
import java.util.ArrayList;
import java.util.List;
List<String> students = new ArrayList<>();
students.add("伊凡");
students.add("瑪麗亞");
students.add("阿列克謝");
搞定!現在可以隨意新增學生,不必再擔心陣列大小。
3. 集合能解決的典型任務
集合是處理資料的瑞士刀。它們可以解決以下問題:
- 儲存動態資料清單: 例如學生名單、排程中的工作、聊天訊息。
- 搜尋與篩選: 快速找到元素、檢查其是否存在、取得符合某條件的所有元素。
- 排序: 依所需準則輕鬆排序元素。
- 新增與刪除元素: 可在集合的任意位置插入與刪除元素,而不需要手動複製陣列。
- 依鍵分組: 例如電話簿,每個名字對應一個電話號碼。
- 保證唯一性: 例如文字中所有不重複單字的集合。
範例:電話簿
用陣列:
- 如何依名字找到號碼?得逐一遍歷陣列並比較名字。
- 如何新增一組配對?必須擴充陣列。
- 如何確保名字不重複?更麻煩。
用集合:
- 使用 Map<String, String>,上述需求都能「開箱即用」。
4. 主要集合類型概覽
在 Java 中,集合大致分為三大類:
| 集合類型 | 介面/類別 | 用途 |
|---|---|---|
| 清單 | List, ArrayList | 有序的元素集合,允許重複,支援索引存取 |
| 集合(Set) | Set, HashSet | 只保存唯一元素,不保證順序 |
| 對映 | Map, HashMap | 儲存鍵-值對,依鍵快速查找 |
清單(List)
- 有序的集合,允許重複。
- 可透過索引取得元素。
- 範例:ArrayList、LinkedList。
集合(Set)
- 只保存唯一元素。
- 不支援索引存取。
- 範例:HashSet、TreeSet。
對映(Map)
- 儲存鍵-值對。
- 依鍵快速查找。
- 範例:HashMap、TreeMap。
視覺化示意(非常粗略):
+------------------+ +-------------------+ +---------------------+
| List | | Set | | Map |
|------------------| |-------------------| |---------------------|
| [a, b, c, d, a] | | {a, b, c, d} | | {a=1, b=2, c=3} |
| 索引:有 | | 索引:無 | | 依鍵查找 |
| 重複:可 | | 重複:不可 | | 鍵唯一 |
+------------------+ +-------------------+ +---------------------+
5. 實用細節
何時使用哪一種集合?
List —— 當元素順序重要、需要允許重複、需要索引存取時(例如待辦清單、訊息歷史)。
Set —— 當只需要唯一元素且不在乎順序時(例如唯一使用者的集合)。
Map —— 當需要將鍵與值配對時(例如電話簿:名字是鍵,電話是值)。
生活中的比喻
List —— 自助餐的排隊:先到先服務,也能重複排隊(允許重複)。
Set —— 派對賓客名單:每位賓客只會出現一次(唯一)。
Map —— 通訊錄:每個名字都有自己的電話。
速查表:集合 vs 陣列
| 陣列(int[]) | 集合(List<Integer>) | |
|---|---|---|
| 大小 | 固定 | 動態 |
| 新增元素 | 麻煩 | 簡單:add() |
| 刪除元素 | 麻煩 | 簡單:remove() |
| 依值查找 | 手動逐一檢查 | 方法:contains() 等 |
| 排序 | 使用 Arrays.sort() | 使用 Collections.sort(),或集合的方法 |
| 唯一性支援 | 無 | 使用 Set |
| 鍵-值對 | 無 | 使用 Map |
6. 集合與 OOP 的關係
集合是實作特定介面(List、Set、Map)的物件。這代表你可以:
- 在集合中存放任何物件,包括你自訂類別的實例。
- 建立集合的集合(例如清單的清單)。
- 將集合用作方法參數與回傳值。
- 透過繼承與組合擴充集合的功能。
範例:自訂類別物件的集合
import java.util.ArrayList;
import java.util.List;
class Student {
String name;
int age;
// 建構子、getter/setter 等等
}
public class Main {
public static void main(String[] args) {
List<Student> group = new ArrayList<>();
group.add(new Student("伊凡", 20));
group.add(new Student("瑪麗亞", 21));
// 以此類推...
}
}
7. 使用集合時的常見錯誤
錯誤 №1:使用未指定型別的集合(raw types)。
如果你寫的是 ArrayList list = new ArrayList(),那麼新增任何物件(例如字串與數字混在一起)時編譯器都不會報錯,但之後嘗試取出元素並轉型成需要的型別時,就可能在執行期發生錯誤(ClassCastException)。務必使用泛型:ArrayList<String> list = new ArrayList<>()。
錯誤 №2:忘了匯入所需類別。
如果你看到「cannot find symbol」的錯誤,請確認檔案開頭有 import java.util.ArrayList; 或你的集合所需的匯入。
錯誤 №3:混淆集合與陣列。
集合不是陣列!集合沒有 length 欄位,請改用 size() 方法。陣列沒有 add() 方法,而集合沒有用於索引存取的 [] 運算子(只有清單可以透過 get(index) 取得)。
錯誤 №4:以為元素順序一定會被保留。
如果你使用 Set 或 Map,元素的順序並不保證(除非使用像 LinkedHashSet 或 TreeMap 這類特殊實作)。若需要有序資料,請使用 List 或相應的集合。
錯誤 №5:在集合中使用基本型別。
集合只能保存物件,不能保存基本型別。也就是說不能建立 List<int>,只能 List<Integer>。別忘了包裝類別!
GO TO FULL VERSION