HashSet 底層基於 HashMap,通過 hashCode() + equals() 判斷元素唯一性。利用「List → Set → List」轉換即可去重,但**順序不保證**。
List listWithDup = Arrays.asList("1", "2", "3", "1");
List listWithoutDup = new ArrayList(new HashSet(listWithDup));
// 結果: [1, 2, 3] 順序不定
List listWithoutDup = new ArrayList<>(new HashSet<>(listWithDup));
// 保持首次出現順序
List distinct = listWithDup.stream().distinct().collect(Collectors.toList());
// 或配合 LinkedHashSet 保序
List distinct = listWithDup.stream()
.collect(Collectors.toCollection(LinkedHashSet::new))
.stream().collect(Collectors.toList());
@Data @AllArgsConstructor
class User {
private Long id;
private String name;
// 必須重寫 hashCode & equals,否則去重失效!
}
@EqualsAndHashCode。id 去重 → @EqualsAndHashCode(of = "id")。| 場景 | 推薦方案 | 時間複雜度 | 空間複雜度 | 順序 |
|---|---|---|---|---|
| 基本類型/String、無序要求 | HashSet 構造器 | O(n) | O(n) | 無序 |
| 基本類型/String、保持順序 | LinkedHashSet / Stream.distinct() | O(n) | O(n) | 有序 |
| 自定義對象、海量數據 (百萬+) | 並行流 parallelStream().distinct() | O(n/p) | O(n) | 無序 |
| 按特定字段去重 (如 id) | Stream + TreeSet(comparing) / Map(key=字段) | O(n log n) | O(n) | 可控 |
| 原地去重 (不創建新集合) | Iterator + Set 記錄見過元素 | O(n) | O(k) | 有序 |
public static void distinctInPlace(List list) {
Set seen = new HashSet<>();
Iterator it = list.iterator();
while (it.hasNext()) {
if (!seen.add(it.next())) it.remove(); // add 返回 false 表示重複
}
}
list.remove() 必須用 Iterator.remove()。HashSet 允許單個 null,TreeSet 不允許。new ArrayList<>(expectedSize) 避免擴容。「List → Set → List」是最簡潔去重模式;需保序用 LinkedHashSet 或 Stream.distinct();自定義對象**務必正確重寫 hashCode/equals**。根據數據量、順序要求、字段粒度選擇最優方案,兼顧可讀性與性能。