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

Java 集合深度解析(41):HashSet 實現原理、特點與最佳實踐

     編輯:Java 綜合教學

Java 集合深度解析(41):HashSet 實現原理、特點與最佳實踐

繼 HashMap 之後,本文深入剖析 Set 接口的核心實現 HashSet:底層結構、核心方法源碼、關鍵特性、應用場景與子類擴展。

一、Set 接口回顧

  • 特徵:無重復元素、無序(HashSet)、可為 null
  • 核心方法:add()、remove()、contains()、iterator()、size()
  • 規約:add() 重復返回 false;equals() 相等則 hashCode() 必須相等

二、HashSet 構造方法

public HashSet()                           // 默認容量 16,負載因子 0.75
public HashSet(int initialCapacity)        // 指定初始容量
public HashSet(int initialCapacity, float loadFactor) // 完全自定義
public HashSet(Collection<? extends E> c)  // 從集合構建,容量 = max(c.size()/.75f + 1, 16)

三、核心源碼解析(基於 JDK 8)

內部結構

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {
    private transient HashMap<E, Object> map;  // 核心:委託 HashMap
    private static final Object PRESENT = new Object(); // 統一的 Value
    ...
}

添加元素

public boolean add(E e) {
    return map.put(e, PRESENT) == null;  // put 返回 null 表示無舊值,即添加成功
}

判斷包含

public boolean contains(Object o) {
    return map.containsKey(o);
}

刪除元素

public boolean remove(Object o) {
    return map.remove(o) == PRESENT;  // 返回值為 PRESENT 表示原有鍵值對
}

迭代器

public Iterator<E> iterator() {
    return map.keySet().iterator();  // 直接返回 KeySet 迭代器
}

四、關鍵特性總結

特性表現底層原因
去重自動過濾重復元素HashMap Key 唯一性
無序迭代順序不固定,可能隨擴容變化HashMap 哈希分佈
高性能add/remove/contains 平均 O(1)哈希表直接定位
允許 null最多一個 null 元素HashMap 支持 null key
非線程安全多線程需外部同步或用 Collections.synchronizedSetHashMap 非線程安全

五、hashCode/equals 契約實戰

class Spec {
    String size;
    String color;
    public Spec(String size, String color) { this.size = size; this.color = color; }

    @Override public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Spec)) return false;
        Spec spec = (Spec) o;
        return Objects.equals(size, spec.size) && Objects.equals(color, spec.color);
    }
    @Override public int hashCode() {
        return Objects.hash(size, color);
    }
    @Override public String toString() { return "[size=" + size + ", color=" + color + "]"; }
}

// 測試
Set<Spec> set = new HashSet<>();
set.add(new Spec("M", "red"));
set.add(new Spec("M", "red"));  // 重復
System.out.println(set);  // 輸出: [[size=M, color=red]] 只有 1 個

警告:未重寫 hashCode/equals 時,兩個內容相同的對象會被視為不同元素,導致去重失效。

六、典型應用場景

  1. 數據去重:日志 IP 去重、ID 列表去重、流式數據攔截重復
  2. 特殊值集合:黑名單/白名單(IP、用戶 ID、敏感詞)、權限標識集合
  3. 集合運算:交集 retainAll()、並集 addAll()、差集 removeAll() — 如用戶標簽相似度計算(交集/並集 = Jaccard 相似係數)

七、有序需求的替代方案

八、小結與預告

HashSet 以 HashMap 為底層,通過「Key 存元素、Value 統一佔位」的巧妙設計,實現了高性能、自動去重、無序的 Set 語義。掌握其 hashCode/equals 契約是正確使用的前提。

下一站:LinkedHashSet(保持插入順序)與 TreeSet(有序性)的樹形實現機制,敬請期待。


關注「老馬說編程」微信公眾號,從入門到高級,深入淺出探索 Java 本質。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
需求實現類原理性能
保持插入順序LinkedHashSetHashMap + 雙向鏈表略低於 HashSet
自然順序/定制排序TreeSet紅黑樹O(log n)
枚舉類型專用EnumSet位向量極快、內存極小