1. HashSet继承关系
2. HashSet概述
HashSet 实现了由hash表支持的 Set 接口(实际上是一个HashMap实例),它不保证集合的迭代顺序,特别是它不保证该顺序恒久不变。HashSet 允许使用 null 元素。
3.HashSet实现
对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成的。
(1)HashSet基本属性
- 底层使用 HashMap 来保存所有元素:
HashMap<E,Object> map
- 定义一个虚拟的对象作为 HashMap 的 value:
Object PRESENT = new Object()
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
// 使用HashMap来保存元素
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map(要与支持映射中的对象关联的伪值)
private static final Object PRESENT = new Object();
......
(2)构造方法
1)public HashSet()
默认无参构造器,构造一个空集合。
注意:底层实际初始化了一个默认容量为 16,负载因子为 0.75 的空HashMap
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
2)public HashSet(Collection<? extends E> c)
构造一个包含指定collection中的元素的集合。
底层实际创建了一个默认负载因子为0.75
和足以容纳指定collection中所有元素的初始容量的HashMap。
初始容量设置:指定集合除以0.75与16比较取最大值 Math.max((int) (c.size()/.75f) + 1, 16)
。
/**
* Constructs a new set containing the elements in the specified
* collection. The <tt>HashMap</tt> is created with default load factor
* (0.75) and an initial capacity sufficient to contain the elements in
* the specified collection.
*
* @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);
}
3)public HashSet(int initialCapacity, float loadFactor)
构造指定容量和负载因子的空集合。
底层实际创建了一个指定容量和负载因子的HashMap。
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and the specified load factor.
*
* @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);
}
4)public HashSet(int initialCapacity)
构造指定容量的空集合。
底层实际创建了一个指定容量以及默认负载因子为0.75
的空集合。
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and default load factor (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);
}
5)HashSet(int initialCapacity, float loadFactor, boolean dummy)
构造一个指定容量和负载因子的空链表哈希集合。此构造函数为包访问权限,不对外公开,实际只是对LinkedHashSet的支持。
底层实际创建了一个指定容量和负载因子的空LinkedHashMap。
/**
* Constructs a new, empty linked hash set. (This package private
* constructor is only used by LinkedHashSet.) The backing
* HashMap instance is a LinkedHashMap with the specified initial
* capacity and the specified load factor.
*
* @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);
}
(3)迭代器
返回对此set中元素进行迭代的迭代器,返回元素的顺序并不是特定的。
注意:底层实际调用 HashMap 的
keySet()
来返回所有的key,可见 HashSet 中元素只是存放在了底层 HashMap 的key上。
/**
* Returns an iterator over the elements in this set. The elements
* are returned in no particular order.
*
* @return an Iterator over the elements in this set
* @see ConcurrentModificationException
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
(4)元素包含
判断集合set中是否包含指定元素。实际上是判断set中包含一个元素e,满足(o==null?e==null:o.equals(e))
时,返回true。
底层实际使用HashaMap的containsKey(o)
方法判断。
/**
* Returns <tt>true</tt> if this set contains the specified element.
* More formally, returns <tt>true</tt> if and only if this set
* contains an element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this set is to be tested
* @return <tt>true</tt> if this set contains the specified element
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
(5)添加元素
- 如果此set中不包含指定元素,则添加该元素到集合中。实际上是判断set中不包含一个元素e,满足
(o==null?e==null:o.equals(e))
时,向set中添加元素。 - 底层实际是调用方法
map.put(e, PRESENT)
,将该元素的 key 放入 hashMap,由于 HashMap 的 put 放入 key-value 时,当新放入 HashMap 的 Entry 中 key 与集合中原有 Entry 中 key 相同(key.hashCode()
返回值相等,通过equals()
比较也返回true),新添加的Entry的value值会覆盖原来的Entry的value值,但key不会变化。
结论:如果向HashSet中添加已经存在的元素时,新添加的元素不会被放入HashMap中,原来的元素也不会有任何改变,这就满足了set中元素不重复的特性。
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns.
*
* @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;
}
(6)删除元素
如果此set中包含指定元素,则将该元素移除。实际上是判断set中包含一个元素e,满足(o==null?e==null:o.equals(e))
时,将其移除。
底层实际调用 HashMap 的remove()
方法删除Entry。
/**
* Removes the specified element from this set if it is present.
* More formally, removes an element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>,
* if this set contains such an element. Returns <tt>true</tt> if
* this set contained the element (or equivalently, if this set
* changed as a result of the call). (This set will not contain the
* element once the call returns.)
*
* @param o object to be removed from this set, if present
* @return <tt>true</tt> if the set contained the specified element
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
(7)清空集合
移除set中所有元素。底层实际调用HashMap的clear()
方法清空所有元素。
/**
* Removes all of the elements from this set.
* The set will be empty after this call returns.
*/
public void clear() {
map.clear();
}
(8)复制
返回此HashSet实例的浅表副本,并没有复制这些元素本身。底层实际调用HashMap的clone()
方法,获取HashMap的浅表副本,并设置到HashSet中。
/**
* Returns a shallow copy of this <tt>HashSet</tt> instance: the elements
* themselves are not cloned.
*
* @return a shallow copy of this set
*/
@SuppressWarnings("unchecked")
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
4. 说明
- 相关 HashMap 的实现原理,请参考Java集合 - HashMap。
- 对于 HashSet 中保存的对象,请注意正确重写其
equals()
和hashCode()
方法,以保证放入的对象的唯一性。