| 維度 | HashSet | HashMap |
|---|---|---|
| 實現接口 | Set | Map |
| 存儲結構 | 鍵值對,值固定為 PRESENT 對象 | 鍵值對,鍵值任意 |
| 去重依據 | 鍵(元素本身) | 鍵 |
| 允許 null | 單個 null 元素 | 單個 null 鍵,多個 null 值 |
| 添加方法 | add(E e) → 返回 boolean | put(K, V) → 返回舊值 |
| 遍歷方式 | Iterator / for-each | keySet / entrySet / values |
| 線程安全 | 非安全 | 非安全 |
| 典型場景 | 去重集合、成員資格測試 | 鍵值映射、緩存、索引 |
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {
private transient HashMap<E, Object> map;
private static final Object PRESENT = new Object();
public HashSet() { map = new HashMap<>(); }
public boolean add(E e) { return map.put(e, PRESENT) == null; }
public boolean contains(Object o) { return map.containsKey(o); }
public boolean remove(Object o) { return map.remove(o) == PRESENT; }
public Iterator<E> iterator() { return map.keySet().iterator(); }
public int size() { return map.size(); }
// ... 其他方法均委託 map
}
兩者均依賴 hashCode() 定位桶位,equals() 比較鍵是否相等。
add(e) 時若鍵已存在(hashCode 相等且 equals 為 true),put 返回舊值非 null,add 返回 false → 去重成功。put(k, v) 若鍵已存在,覆蓋舊值並返回舊值;鍵不存在返回 null。class Person {
String name; int age;
// 必須重寫 hashCode() 和 equals(),且保持一致
@Override public boolean equals(Object o) { ... }
@Override public int hashCode() { return Objects.hash(name, age); }
}
否則:HashSet 無法去重,HashMap 無法正確查找。
HashSetHashMapLinkedHashSet / LinkedHashMapTreeSet / TreeMapCollections.synchronizedSet/Map 或 ConcurrentHashMap / CopyOnWriteArraySetHashSet 就是鍵為元素、值固定為 PRESENT 的 HashMap 封裝。理解這層關係,所有 API 行為(去重、遍歷、性能)自然通透。選型核心:集合用 Set,映射用 Map,不要為了去重硬用 Map,也不要為了存鍵值對硬用 Set。