1 Set
Set 集合类似于一个罐子,程序可以依次把多个对象“丢进”Set 集合,而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。
Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类。
2. HashSet 概述
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。
2.1 HashSet的特点
- 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
- HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。
- 集合元素值可以是 null。
- 由哈希表(实际上是一个HashMap实例)支持。当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。3. HashSet实现原理
3.1 属性
// 定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。
private static final Object PRESENT = new Object();
3.2 构造函数
```java //构造一个新的空集合;备份HashMap实例具有默认初始容量(16)和负载因子(0.75) public HashSet() {
}map = new HashMap<>();
//构造包含指定集合中元素的新集合。HashMap是使用默认加载因子(0.75)和初始容量创建的,初始容量足以包含指定集合中的元素 public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } …
<a name="NzocI"></a>
## 3.3 add
```java
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
如果此set中尚未包含指定元素,则添加指定元素。 更确切地讲,如果此 set 没有包含满足(e==null ? e2==null : e.equals(e2)) 的元素e2,则向此set 添加指定的元素e。 如果此set已包含该元素,则该调用不更改set并返回false。
底层实际将将该元素作为key放入HashMap。 由于HashMap的put()方法添加key-value对时,当新放入HashMap的Entry中key 与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true), 新添加的Entry的value会将覆盖原来Entry的value,但key不会有任何改变, 因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中, 原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。
3.4 remove
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
如果指定元素存在于此set中,则将其移除,更确切地讲,如果此set包含一个满足(o==null ? e==null : o.equals(e))的元素e, 则将其移除。如果此set已包含该元素,则返回true(或者:如果此set因调用而发生更改,则返回true),(一旦调用返回,则此set不再包含该元素)。 底层实际调用HashMap的remove方法删除指定Entry。
[
](https://blog.csdn.net/guoweimelon/article/details/50804799)