HashSet介绍
- 实现了Serializable接口,表明它支持序列化。
- 实现了Cloneable接口,表明它支持克隆,可以调用超类的clone()方法进行浅拷贝。
- 继承了AbstractSet抽象类,和ArrayList和LinkedList一样,在他们的抽象父类中,都提供了equals()方法和hashCode()方法。它们自身并不实现这两个方法,(但是ArrayList和LinkedList的equals()实现不同。你可以看我的关于ArrayList这一块的源码解析)这就意味着诸如和HashSet一样继承自AbstractSet抽象类的TreeSet、LinkedHashSet等,他们只要元素的个数和集合中元素相同,即使他们是AbstractSet不同的子类,他们equals()相互比较的后的结果仍然是true。
```java
public boolean equals(Object o) {
}if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
//必须保证元素的个数相等。
if (c.size() != size())
return false;
try {
//调用了AbstractCollection的方法。
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
public boolean containsAll(Collection<?> c) {
//只需要逐个判断集合是否包含其中的元素。
for (Object e : c)
if (!contains(e))
return false;
return true;
}
4. 实现了Set接口。
<a name="JfgY5"></a>
## 源码分析
**HashSet类**<br />我们可以看到HashSet有多个构造函数,但每个构造函数都是初始化了一个HashMap的对象,所以**HashSet内部是使用HashMap来存储对象的。**
```java
/**
* 构造一个新的空集合;
* 备份HashMap实例具有默认的初始容量(16)和负载因子(0.75)。
*/
public HashSet() {
map = new HashMap<>();
}
/**
* 构造包含指定集合中元素的新集合。
* HashMap是使用默认加载因子(0.75)创建的,初始容量足以包含指定集合中的元素。
*
* @param c the collection whose elements are to be placed into this set
* @throws NullPointerException if the specified collection is null
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* 构造一个新的空集合;
* 备份HashMap实例具有指定的初始容量和指定的负载因子。
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
/**
* 构造一个新的空集合;
* 备份HashMap实例具有指定的初始容量和默认负载因子(0.75)。
*
* @param initialCapacity the initial capacity of the hash table
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* 构造一个新的空链接哈希集。(此包专用构造函数仅由LinkedHashSet使用。)
* 备份HashMap实例是具有指定初始容量和指定负载因子的LinkedHashMap。
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @param dummy ignored (distinguishes this
* constructor from other int, float constructor.)
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
HashSet中使用的HashMap,key为Set的元素类型,value为Object。
private transient HashMap<E,Object> map;
add(E e)
/**
* 如果指定的元素尚未存在,则将其添加到此集合。
* 更正式地说,如果此集合不包含元素e2,则将指定的元素e添加到此集合,
* 从而(e==null?e2==null:e.equals(e2))。
* 如果此集合已经包含元素,则调用将保持集合不变并返回false。
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
**/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
PRESENT的定义
private static final Object PRESENT = new Object();
PRESENT是一个静态的类对象,Object类型。所有HashSet的实例都共享这个对象。
也就是说,我们在向set中添加一个e元素的时候,实际上就是在像map添加一个(e, Object)的键值对。我们添加的元素e变成了map中的key,而value则都是Obeject对象。又因为map中key值是唯一的,而value是可以重复的。所以我们添加的e作为key的话,就可以保证添加成功的话,e一定是唯一的。这就实现了set的唯一性。
remove(Object o)
/**
* 从该集合中删除指定的元素(如果存在)。
* 更正式地说,如果这个集合包含这样一个元素,那么移除一个元素e,
* 使得(o==null?e==null:o.equals(e))。
* 如果此集合包含元素,则返回true(如果此集合因调用而更改,则返回等效值)。
* (一旦调用返回,此集合将不包含元素。)
**/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
Hashmap中移除一个key的话,会返回这个key值锁对应的value,而我们这里的map,所有的key的value都是同一个对象PRESENT,所以我们这里只需要判断map.remove(o)的返回值是不是PRESENT,就可以确定是否成功移除了。
terator()
hashSet没有get方法,想要获取HashSet的元素需要调用iterator()。因为添加的元素值都被map当成了key来存储,显然没有从map中获取单独一个key的方法,但是我们可以获取所有key,调用keySet方法即可。
/**
* 返回此集合中元素的迭代器。元素不按特定顺序返回。
*
* @return an Iterator over the elements in this set
* @see ConcurrentModificationException
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
小结
- hashSet内部用HashMap来存储元素
- HashSet利用本身的值来计算hash值,因为值被当作hashmap的key,而hashmap是利用key来计算hash值的
- 因为hashset将value当作key来存储,所以根据map的key值唯一的原理,我们就可以实现set的无重复元素的功能