对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码:
1 package java.util;
2
3 public class HashSet<E>
4 extends AbstractSet<E>
5 implements Set<E>, Cloneable, java.io.Serializable
6 {
7 static final long serialVersionUID = -5024744406713321676L;
8
9 // 使用 HashMap 的 key 保存 HashSet 中的所有元素
10 private transient HashMap<E,Object> map;
11
12 // 定义一个虚拟的 Object 对象作为 HashMap 的 value
13 private static final Object PRESENT = new Object();
14
15 // 初始化一个 HashSet ,底层会初始化一个 HashMap
16 public HashSet() {
17 map = new HashMap<>();
18 }
19
20 public HashSet(Collection<? extends E> c) {
21 map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
22 addAll(c);
23 }
24
25 // 以指定的 initialCapacity 和 loadFactor 创建一个 HashSet
26 // 其实就是以相应的参数创建 HashMap
27 public HashSet(int initialCapacity, float loadFactor) {
28 map = new HashMap<>(initialCapacity, loadFactor);
29 }
30
31 public HashSet(int initialCapacity) {
32 map = new HashMap<>(initialCapacity);
33 }
34
35 HashSet(int initialCapacity, float loadFactor, boolean dummy) {
36 map = new LinkedHashMap<>(initialCapacity, loadFactor);
37 }
38
39 // 调用 map 的 keySet 来返回所有的 key
40 public Iterator<E> iterator() {
41 return map.keySet().iterator();
42 }
43
44 // 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 HashSet 里的元素的个数
45 public int size() {
46 return map.size();
47 }
48
49 // 调用 HashMap 的 isEmpty() 方法判断该 map 是否为空
50 // 当 map 为空时,对应的 HashSet 也为空
51 public boolean isEmpty() {
52 return map.isEmpty();
53 }
54
55 // 调用 HashMap 的 containsKey() 判断是否包含指定的 key
56 // HashSet 的所有元素就是通过 HaseMap 的 key 保存的
57 public boolean contains(Object o) {
58 return map.containsKey(o);
59 }
60
61 // 将指定元素放入 HashSet 中,也是将该元素作为 key 存入到 HashMap 中
62 public boolean add(E e) {
63 return map.put(e, PRESENT)==null;
64 }
65
66 // 调用 HashMap 的 remove(o) 删除指定的 Entry ,也就是删除了 HashSet 对应的元素
67 public boolean remove(Object o) {
68 return map.remove(o)==PRESENT;
69 }
70
71 // 调用 HashMap 的 clear() 清空所有的 Entry ,也就清空 HashSet 中的所有元素
72 public void clear() {
73 map.clear();
74 }
75
76 ...
77
78 }
由上面源程序可以看出,HashSet 的实现其实非常简单,它只是封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。
HashMap 的 put 与 HashSet 的 add
由于 HashSet 的 add() 方法添加集合元素时实际上转变为调用 HashMap 的 put() 方法来添加 key-value 对,当新放入 HashMap 的 Entry 中 key 与集合中原有 Entry 的 key 相同(hashCode() 返回值相等,通过 equals 比较也返回 true),新添加的 Entry 的 value 将覆盖原来 Entry 的 value,但 key 不会有任何改变,因此如果向 HashSet 中添加一个已经存在的元素,新添加的集合元素(底层由 HashMap 的 key 保存)不会覆盖已有的集合元素。