首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java Set 接口實戰指南:HashSet、TreeSet、LinkedHashSet 核心差異與最佳實踐

     編輯:Java 綜合教學

Set 三大實現類核心差異

特性HashSetLinkedHashSetTreeSet
底層結構HashMapHashMap + 雙向鏈表TreeMap (紅黑樹)
迭代順序無序 (雜湊分佈)插入順序排序順序 (自然/比較器)
查找/新增/刪除O(1) 平均O(1) 平均O(log n) 保證
Null 元素允許 1 個允許 1 個自然排序不允許
記憶體開銷中等較高 (額外鏈表指標)最高 (樹節點)
適用場景純去重、高頻查找需保持插入序、LRU 快取需排序、區間查詢、排名

HashSet:正確覆寫 hashCode() 與 equals()

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() 失效、無法移除。

TreeSet:兩種排序策略

策略一:自然排序 (實作 Comparable)

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]]

策略二:客製比較器 (Comparator)

// 學生類別無需實作 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()));

LinkedHashSet:保持插入順序的去重神器

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 也能排序:Collections.sort() 靈活運用

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));

避坑指南

  • 可變物件放入 Set:若物件在 Set 中被修改導致 equals/hashCode 變化,Set 行為未定義 (找不到、無法移除)。解法:存入不可變物件、或移除後修改再加入。
  • TreeSet 比較器與 equals 不一致:compare(a,b)==0 但 !a.equals(b) → 違反 Set 契約,導致邏輯重複。原則:比較器判定相等必須蘊含 equals 相等。
  • 大量資料去重:優先 HashSet,預估容量避免擴容 new HashSet<>(expectedSize * 4/3)。
  • 並發環境:用 ConcurrentHashMap.newKeySet() 或 Collections.synchronizedSet()。

轉載自:liuling 博客

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved