10. Java 中的 HashSet 和 HashMap 有什么区别?
HashSet
和 HashMap
是 Java 集合框架中的两个重要类,它们都基于哈希表(Hash Table)实现,并且在许多方面共享类似的特性。然而,它们的用途和实现上有一些重要的区别。
1. 功能和用途
-
HashSet
:-
HashSet
是一个实现了Set
接口的集合类,用于存储唯一的元素。集合中的元素不能重复。 -
它不保证集合的顺序(插入顺序也不保证),并且不允许存储
null
元素(在某些实现中允许一个null
元素,但如果多次插入null
,也只会保留一个null
)。 -
主要用于需要确保元素唯一性的场景。
-
-
HashMap
:-
HashMap
是一个实现了Map
接口的集合类,用于存储键值对(key-value pair)。每个键是唯一的,但多个键可以映射到相同的值。 -
HashMap
不保证键值对的顺序,也允许一个null
键和多个null
值。 -
主要用于需要通过键快速查找值的场景。
-
2. 内部实现
-
HashSet
的实现:-
HashSet
实际上是基于HashMap
实现的。每当一个元素被添加到HashSet
中时,HashSet
将这个元素作为HashMap
的键存储,而HashMap
的值是一个常量PRESENT
(通常是一个静态的Object
)。
private static final Object PRESENT = new Object(); private transient HashMap<E,Object> map; public boolean add(E e) { return map.put(e, PRESENT) == null; }
-
-
HashMap
的实现:-
HashMap
使用哈希表来存储键值对。键通过hashCode()
方法计算哈希值,并且哈希冲突通过链表或红黑树处理(在 Java 8 及之后的版本中,链表长度超过一定阈值后会转换为红黑树)。
-
3. 主要方法
-
HashSet
:-
add(E e)
: 添加元素到集合中,如果元素已经存在,则不改变集合,返回false
。 -
remove(Object o)
: 从集合中删除指定元素,返回是否成功删除。 -
contains(Object o)
: 检查集合中是否包含指定元素,返回true
或false
。 -
size()
: 返回集合中元素的数量。
-
-
HashMap
:-
put(K key, V value)
: 将键值对存储到HashMap
中,如果键已经存在,则更新其对应的值,返回旧值。 -
get(Object key)
: 根据键查找并返回对应的值,如果键不存在则返回null
。 -
remove(Object key)
: 删除指定键及其对应的值,返回被删除的值。 -
containsKey(Object key)
: 检查是否存在指定的键,返回true
或false
。 -
containsValue(Object value)
: 检查是否存在指定的值,返回true
或false
。 -
size()
: 返回HashMap
中键值对的数量。
-
4. 使用场景
-
HashSet
的典型使用场景:-
当需要存储一组唯一的元素时,如:去除列表中的重复值、检查某个元素是否存在于集合中等。
HashSet<String> set = new HashSet<>(); set.add("Apple"); set.add("Banana"); set.add("Apple"); // 该操作不会重复添加 "Apple"
-
-
HashMap
的典型使用场景:-
当需要通过键快速查找值时,如:实现字典、缓存等功能。
HashMap<String, Integer> map = new HashMap<>(); map.put("Apple", 1); map.put("Banana", 2); int count = map.get("Apple"); // 返回 1
-
5. 性能
-
时间复杂度:
-
HashSet
和HashMap
的常见操作(如add
、remove
、contains
)的平均时间复杂度都是 O(1),但在最坏情况下(哈希冲突严重)可能退化为 O(n)。 -
HashMap
的查找和插入操作由于涉及键值对,性能可能稍微复杂一些,但总体而言与HashSet
类似。
-
-
空间复杂度:
-
HashMap
由于存储了键值对,因此比HashSet
占用更多的内存空间。
-
总结
-
HashSet
用于存储唯一的元素,内部使用HashMap
实现,其主要关注的是元素的唯一性和快速查找。 -
HashMap
用于存储键值对,支持通过键快速访问值,并允许一个键对应一个值。 -
选择
HashSet
还是HashMap
主要取决于你的需求:如果只需要唯一的元素集合,选择HashSet
;如果需要通过键来映射和查找值,选择HashMap
。