目录
简介
介绍
方法源码
迭代方式
-----------源码分析基于jdk1.8.
HashSet自jdk1.2引入,继承了抽象方法AbstractSet,实现了Set接口,Cloneable接口,以及java.io.Serializable接口,所以能被克隆和序列化。
HashSet底层是使用HashMap实现的,其中HashMap的键就是HashSet集合的元素,所以HashSet集合是一个元素不重复的集合,并且不保证元素的顺序,容许存储null值。
此外HashSet不是同步的,在多线程环境中使用时,需要外部进行同步处理,最好在创建时使用Set s = Collections.synchronizedSet(new HashSet(...)),以防止对于Set偶发非同步访问。
1.构造方法
//构建一个HashSet,底层使用的HashMap实例是默认的初始大小16,负载因子为0.75
public HashSet() {
map = new HashMap<>();
}
//构建一个包含指定集合元素的HashSet,底层使用的HashMap是默认负载因子(0.75)以及能容纳指定集合的元素的初始容量
//如果指定集合c为null,将抛出NullPointerException异常.
public HashSet(Collection extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
//构建一个HashSet,底层使用的HashMap实例具有指定的初始容量和指定负载因子。
//初始容量小于0或者负载因子为非正数,将抛出IllegalArgumentException异常。
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
//构建一个HashSet,底层使用的HashMap实例具有指定初始容量和默认的负载因子(0.75)
//如果指定的初始容量小于0,将抛出IllegalArgumentException异常。
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
//构建一个HashSet,此构造器只限于LinkedHashSet使用,底层使用是LinkedHashMap,具有指定初始容量和指定的负载因子。
//初始容量小于0或者负载因子为非正数,将抛出IllegalArgumentException异常。
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
HashSet的底层使用HashMap实现的,所以受到初始容量initialCapacity和负载因子LoadFactor的影响,构造有参构造方法时传入这两个变量的值,以调整底层HashMap的性能。默认的负载因子是0.75,是时间成本和空间成本上的一种权衡。HashMap中如果没有指定capacity,默认容量是16,在指定两个参数的情况下可以通过来计算capacity,第二个构造方法中Math.max((int) (c.size()/.75f) + 1, 16),表示计算后容量没有超过16情况下,HashMap的初始容量设置为16。
2.内部变量
//HashSet底层就是通过HashMap来保存数据,HashSet的元素是HashMap中的值。
private transient HashMap map;
// Dummy value to associate with an Object in the backing Map
//HashMap保存的数据是键-值对映射,HashSet中的值是HashMap的键
//而HashMap的值用固定的对象PRESENT来代替
private static final Object PRESENT = new Object();
HashMap存储的是键-值对映射,HashSet集合中的元素就是HashMap的键,而HashMap的值就是用固定的Object对象PRESENT来代替。
Hashset方法实现依靠HashMap,可以看到HashSet中大部分的方法都会直接调用HashMap中的方法。
//返回HashSet的迭代器,元素没有顺序。
public Iterator iterator() {
return map.keySet().iterator();
}
//返回此HashSet中元素的数量.
public int size() {
return map.size();
}
//如果此HashSet不包含任何元素,返回true.
public boolean isEmpty() {
return map.isEmpty();
}
//如果此HashSet包含指定的元素o,将返回true
public boolean contains(Object o) {
return map.containsKey(o);
}
//向HashSet中添加指定的元素e,添加成功返回true。
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//从HashSet中移除指定的元素o.
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
//清空HashSet中所有的元素
public void clear() {
map.clear();
}
//返回此HashSet的浅度克隆
@SuppressWarnings("unchecked")
public Object clone() {
try {
HashSet newSet = (HashSet) super.clone();
newSet.map = (HashMap) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
//将此HashSet实例状态保存到流中(序列化)
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// Write out HashMap capacity and load factor
//HashMap的容量和负载因子
s.writeInt(map.capacity());
s.writeFloat(map.loadFactor());
// Write out size
//HashMap的实际大小
s.writeInt(map.size());
// Write out all elements in the proper order.
//所有的元素
for (E e : map.keySet())
s.writeObject(e);
}
//从流中重新构建一个HashSet(反序列化)
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read capacity and verify non-negative.
//读取容量
int capacity = s.readInt();
if (capacity < 0) {
throw new InvalidObjectException("Illegal capacity: " +
capacity);
}
// Read load factor and verify positive and non NaN.
//读取负载因子
float loadFactor = s.readFloat();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
// Read size and verify non-negative.
//读取实际大小
int size = s.readInt();
if (size < 0) {
throw new InvalidObjectException("Illegal size: " +
size);
}
// Set the capacity according to the size and load factor ensuring that
// the HashMap is at least 25% full but clamping to maximum capacity.
capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);
// Constructing the backing map will lazily create an array when the first element is
// added, so check it before construction. Call HashMap.tableSizeFor to compute the
// actual allocation size. Check Map.Entry[].class since it's the nearest public type to
// what is actually created.
SharedSecrets.getJavaOISAccess()
.checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));
// Create backing HashMap
//创建底层使用的是HashMap
map = (((HashSet>)this) instanceof LinkedHashSet ?
new LinkedHashMap(capacity, loadFactor) :
new HashMap(capacity, loadFactor));
// Read in all elements in the proper order.
for (int i=0; i spliterator() {
return new HashMap.KeySpliterator(map, 0, -1, 0, 0);
}
1.通过Iterator迭代器遍历HashSet,调用方法iterator()返回迭代器。
2.使用foreach循环。
public class IteratorHashSet {
public static void main(String[] args) {
Set sets = new HashSet<>();
for(String word:"one,two,three,four".split(",")) {
sets.add(word);
}
iteratorHashSet1(sets);
iteratorHashSet2(sets);
}
private static void iteratorHashSet1(Set set) {
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+" ");
}
System.out.println("--------");
}
private static void iteratorHashSet2(Set set) {
for(String str:set) {
System.out.print(str+" ");
}
System.out.println("----------");
//jdk1.8
set.forEach(s->System.out.print(s+" "));
System.out.println("----------");
set.forEach(System.out::print);
}
}