一、官方介绍
这个类实现Set接口,由一个 hash table (实际上是一个HashMap实例)支持。它不保证集合的迭代顺序;特别是,它不能保证顺序在一段时间内保持不变。该类允许空元素。这个类为基本操作(添加、删除、包含和大小)提供了常数时间性能,假设hash函数正确地将元素分散到各个bucket中。遍历这个集合需要的时间与HashSet实例的大小(元素的数量)和支持HashMap实例的“容量”(buckets的数量)的总和成比例。因此,如果迭代性能很重要,那么不要将初始容量设置得太高(或负载因子太低)是非常重要的。注意,这个实现不是同步的 。如果多个线程同时访问一个散列集,并且至少有一个线程修改该散列集,则必须在外部对其进行同步。这通常是通过对一些自然封装了集合的对象进行同步来实现的。如果不存在这样的对象,则应该使用Collections.synchronizedSet方法来“包装”Set。这最好在创建时完成,以防止意外的不同步访问集:
Set s
= Collections
.synchronizedSet(new HashSet(...));
这个类的iterator和listIterator方法返回的迭代器是 fail-fast的:如果在创建迭代器之后的任何时候,以任何方式(除了通过迭代器自己的删除或添加方法)对列表进行结构修改,迭代器将抛出ConcurrentModificationException异常。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒任意的、不确定的行为的风险。注意,不能保证迭代器的快速故障行为,因为通常来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。Fail-fast迭代器在最大努力的基础上抛出ConcurrentModificationException。因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的 fail-fast 行为应该只用于检测bug。该类是Java集合框架的成员
二、源码分析
package java
.util
;
import java
.io
.InvalidObjectException
;
import sun
.misc
.SharedSecrets
;
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable
, java
.io
.Serializable
{
static final long serialVersionUID
= -5024744406713321676L
;
private transient HashMap
<E, Object> map
;
private static final Object PRESENT
= new Object();
public HashSet()
{
map
= new HashMap<>();
}
public HashSet(Collection
<? extends E> c
)
{
map
= new HashMap<>(Math
.max((int)(c
.size() / .75f) + 1, 16));
addAll(c
);
}
public HashSet(int initialCapacity
, float loadFactor
)
{
map
= new HashMap<>(initialCapacity
, loadFactor
);
}
public HashSet(int initialCapacity
)
{
map
= new HashMap<>(initialCapacity
);
}
HashSet(int initialCapacity
, float loadFactor
, boolean dummy
)
{
map
= new LinkedHashMap<>(initialCapacity
, loadFactor
);
}
public Iterator
<E> iterator()
{
return map
.keySet().iterator();
}
public int size()
{
return map
.size();
}
public boolean isEmpty()
{
return map
.isEmpty();
}
public boolean contains(Object o
)
{
return map
.containsKey(o
);
}
public boolean add(E e
)
{
return map
.put(e
, PRESENT
) == null
;
}
public boolean remove(Object o
)
{
return map
.remove(o
) == PRESENT
;
}
public void clear()
{
map
.clear();
}
@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
);
}
}
private void writeObject(java
.io
.ObjectOutputStream s
) throws java
.io
.IOException
{
s
.defaultWriteObject();
s
.writeInt(map
.capacity());
s
.writeFloat(map
.loadFactor());
s
.writeInt(map
.size());
for (E e
: map
.keySet())
s
.writeObject(e
);
}
private void readObject(java
.io
.ObjectInputStream s
) throws java
.io
.IOException
, ClassNotFoundException
{
s
.defaultReadObject();
int capacity
= s
.readInt();
if (capacity
< 0)
{
throw new InvalidObjectException("Illegal capacity: " + capacity
);
}
float loadFactor
= s
.readFloat();
if (loadFactor
<= 0 || Float
.isNaN(loadFactor
))
{
throw new InvalidObjectException("Illegal load factor: " + loadFactor
);
}
int size
= s
.readInt();
if (size
< 0)
{
throw new InvalidObjectException("Illegal size: " + size
);
}
capacity
= (int)Math
.min(size
* Math
.min(1 / loadFactor
, 4.0f), HashMap
.MAXIMUM_CAPACITY
);
SharedSecrets
.getJavaOISAccess().checkArray(s
, Map
.Entry
[].class, HashMap
.tableSizeFor(capacity
));
map
= (((HashSet
<?>)this) instanceof LinkedHashSet ? new LinkedHashMap<E, Object>(capacity
, loadFactor
)
: new HashMap<E, Object>(capacity
, loadFactor
));
for (int i
= 0; i
< size
; i
++)
{
@SuppressWarnings ("unchecked")
E e
= (E
)s
.readObject();
map
.put(e
, PRESENT
);
}
}
public Spliterator
<E> spliterator()
{
return new HashMap.KeySpliterator<E, Object>(map
, 0, -1, 0, 0);
}
}