Java HashSet
HashSet 是最常用的 Set 實作,基於 HashMap,元素不重複且無序。
建立 HashSet
import java.util.HashSet;
import java.util.Set;
// 空的 HashSet
Set<String> set1 = new HashSet<>();
// 從集合建立
Set<String> set2 = new HashSet<>(Arrays.asList("A", "B", "C"));
// Java 9+ 建立不可變 Set
Set<String> set3 = Set.of("A", "B", "C");
常用方法
新增元素
Set<String> set = new HashSet<>();
set.add("Apple"); // true
set.add("Banana"); // true
set.add("Apple"); // false(已存在,不會重複加入)
set.addAll(Arrays.asList("C", "D")); // 加入多個
移除元素
Set<String> set = new HashSet<>(Arrays.asList("A", "B", "C"));
set.remove("A"); // 移除元素
set.removeAll(Arrays.asList("B", "C")); // 移除多個
set.removeIf(s -> s.length() > 3); // 條件移除
set.clear(); // 清空
檢查元素
Set<String> set = new HashSet<>(Arrays.asList("A", "B", "C"));
boolean has = set.contains("A"); // true
boolean empty = set.isEmpty(); // false
int size = set.size(); // 3
遍歷
Set<String> set = new HashSet<>(Arrays.asList("A", "B", "C"));
// for-each
for (String s : set) {
System.out.println(s);
}
// forEach + Lambda
set.forEach(System.out::println);
// Iterator
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
集合運算
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4));
Set<Integer> set2 = new HashSet<>(Arrays.asList(3, 4, 5, 6));
// 聯集
Set<Integer> union = new HashSet<>(set1);
union.addAll(set2); // [1, 2, 3, 4, 5, 6]
// 交集
Set<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2); // [3, 4]
// 差集
Set<Integer> difference = new HashSet<>(set1);
difference.removeAll(set2); // [1, 2]
實際應用
去除重複
String[] words = {"apple", "banana", "apple", "cherry", "banana"};
// 轉為 Set 去重
Set<String> uniqueWords = new HashSet<>(Arrays.asList(words));
// [apple, banana, cherry]
// 再轉回 List
List<String> uniqueList = new ArrayList<>(uniqueWords);
快速查找
Set<String> validCodes = new HashSet<>(Arrays.asList("A001", "A002", "B001"));
String input = "A001";
if (validCodes.contains(input)) {
System.out.println("有效的代碼");
}
找出共同元素
List<String> list1 = Arrays.asList("A", "B", "C", "D");
List<String> list2 = Arrays.asList("C", "D", "E", "F");
Set<String> set1 = new HashSet<>(list1);
set1.retainAll(list2);
System.out.println(set1); // [C, D]
檢查是否有重複
public static <T> boolean hasDuplicates(List<T> list) {
Set<T> set = new HashSet<>(list);
return set.size() < list.size();
}
HashSet vs TreeSet vs LinkedHashSet
| 特性 | HashSet | TreeSet | LinkedHashSet |
|---|---|---|---|
| 順序 | 無 | 排序 | 插入順序 |
| 效能 | O(1) | O(log n) | O(1) |
| null | 允許一個 | 不允許 | 允許一個 |
// 需要排序
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
// [1, 2, 3]
// 需要保持插入順序
Set<String> linkedSet = new LinkedHashSet<>();
linkedSet.add("C");
linkedSet.add("A");
linkedSet.add("B");
// [C, A, B]
注意事項
hashCode 和 equals
元素物件必須正確實作 hashCode() 和 equals():
public class Person {
private String name;
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
可變物件
不建議將可變物件作為 Set 的元素,修改後可能無法正確查找。
與陣列轉換
// Set 轉陣列
Set<String> set = new HashSet<>(Arrays.asList("A", "B", "C"));
String[] array = set.toArray(new String[0]);
// 陣列轉 Set
String[] arr = {"X", "Y", "Z"};
Set<String> fromArray = new HashSet<>(Arrays.asList(arr));