| 特性 | HashSet | LinkedHashSet | TreeSet |
|---|---|---|---|
| 底層結構 | HashMap | HashMap + 雙向鏈表 | TreeMap (紅黑樹) |
| 迭代順序 | 無序 (雜湊分佈) | 插入順序 | 排序順序 (自然/比較器) |
| 查找/新增/刪除 | O(1) 平均 | O(1) 平均 | O(log n) 保證 |
| Null 元素 | 允許 1 個 | 允許 1 個 | 自然排序不允許 |
| 記憶體開銷 | 中等 | 較高 (額外鏈表指標) | 最高 (樹節點) |
| 適用場景 | 純去重、高頻查找 | 需保持插入序、LRU 快取 | 需排序、區間查詢、排名 |
public class Student {
private int id;
public Student(int id) { this.id = id; }
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
return id == ((Student) o).id;
}
@Override public int hashCode() {
return Objects.hash(id); // 或 return id;
}
@Override public String toString() { return "Student[" + id + "]"; }
}
// 測試
Set<Student> set = new HashSet<>();
set.add(new Student(1));
set.add(new Student(1)); // 重複,不會加入
set.add(new Student(2));
System.out.println(set); // [Student[1], Student[2]] 順序不定
鐵律:equals()相等 →hashCode()必須相等。
違反會導致:Set 中出現邏輯重複元素、contains()失效、無法移除。
public class Student implements Comparable<Student> {
private int id;
// ... 建構子、getter
@Override public int compareTo(Student o) {
return Integer.compare(this.id, o.id); // 升序
// return o.id - this.id; // 降序 (注意溢位風險,建議用 compare)
}
}
// 使用
Set<Student> set = new TreeSet<>();
set.add(new Student(5));
set.add(new Student(1));
set.add(new Student(3));
System.out.println(set); // [Student[1], Student[3], Student[5]]
// 學生類別無需實作 Comparable
public class Student { /* 僅含 id、equals/hashCode */ }
// 獨立比較器類別
public class StudentIdDescComparator implements Comparator<Student> {
@Override public int compare(Student s1, Student s2) {
return Integer.compare(s2.getId(), s1.getId()); // 降序
}
}
// 使用:建構 TreeSet 時傳入比較器
Set<Student> set = new TreeSet<>(new StudentIdDescComparator());
// 或 Lambda (Java 8+)
Set<Student> set = new TreeSet<>((s1, s2) -> Integer.compare(s2.getId(), s1.getId()));
Set<String> set = new LinkedHashSet<>();
set.add("delta");
set.add("alpha");
set.add("epsilon");
set.add("beta"); // 新增
set.add("alpha"); // 重複,忽略
set.add("gamma");
System.out.println(set); // [delta, alpha, epsilon, beta, gamma] 完全保持插入序
內部維護雙向鏈表串聯所有條目,迭代器按鏈表順序遍歷。LRU 快取 經典實現基礎。
List<Student> list = new ArrayList<>();
list.add(new Student(5));
list.add(new Student(1));
list.add(new Student(3));
// 自然排序 (需實作 Comparable)
Collections.sort(list);
System.out.println(list); // [1, 3, 5]
// 客製比較器排序
Collections.sort(list, (s1, s2) -> Integer.compare(s2.getId(), s1.getId()));
System.out.println(list); // [5, 3, 1]
// 多關鍵字排序 (Java 8+ Comparator 鏈式)
Collections.sort(list, Comparator
.comparingInt(Student::getId)
.thenComparing(Student::getName));
equals/hashCode 變化,Set 行為未定義 (找不到、無法移除)。解法:存入不可變物件、或移除後修改再加入。compare(a,b)==0 但 !a.equals(b) → 違反 Set 契約,導致邏輯重複。原則:比較器判定相等必須蘊含 equals 相等。HashSet,預估容量避免擴容 new HashSet<>(expectedSize * 4/3)。ConcurrentHashMap.newKeySet() 或 Collections.synchronizedSet()。轉載自:liuling 博客