概述api
在分析HashSet源码前,先看看HashSet的继承关系安全
HashSet继承关系
从上图能够看出,HashSet继承自AbstractSet,实现了Set接口,接着看一下源码中的注释this
This class implements the Set interface, backed by a hash table
(actually a HashMapinstance). It makes no guarantees as to the
iteration order of the set; in particular, it does not guarantee that the
order will remain constant over time. This class permits the null element.
HashSet实现了Set接口,内部有一个哈希表支撑(实际上就是一个HashMap实例),它不保证迭代的顺序;尤为是,随着时间的变化,它不能保证set的迭代顺序保持不变。容许插入空值。
到此发现,HashSet实际上能够拆分红Hash跟Set,Hash指的是HashMap,Set则是指实现了Set接口,这样看来,HashSet的实现其实就比较简单了,下面开始分析源码。spa
正文线程
成员变量code
//序列化ID static final long serialVersionUID = -5024744406713321676L; //内置的HashMap private transient HashMap<E,Object> map; // 就是一个傀儡,填充HashMap的Value而已,没有实际意义 private static final Object PRESENT = new Object();
构造方法blog
空的构造方法继承
初始化一个空的HashMap接口
public HashSet() { map = new HashMap<>(); }
带有容量的构造方法ip
HashMap给定一个容量
public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); }
带有容量跟负载因子的构造方法
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor); }
带有容量跟负载因子,以及Value类型区分
dummy做为Value是基本类型跟引用类型,注意此处初始化的是一个LinkedHashMap
HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
经过一个集合初始化
public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); }
调用addAll方法
public boolean addAll(Collection<? extends E> c) { boolean modified = false; //循环遍历 for (E e : c) //若是set中没有此元素,添加成功 if (add(e)) modified = true; return modified; }
增长元素
添加一个元素,若是Map中存在,返回false,不然返回true
public boolean add(E e) { return map.put(e, PRESENT)==null; }
看一下Map的put方法
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key); int i = indexFor(hash, table.length); for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) { Object k; //这里比较了hash值跟equals方法 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
因此Set元素必须复写hashcode跟equals方法,否则会致使元素错乱
删除元素
public boolean remove(Object o) { //直接调用map的方法 return map.remove(o)==PRESENT; }
clear
public void clear() {
//调用map的Clear方法
map.clear(); }
contains方法
public boolean contains(Object o) { 调用map的contains方法 return map.containsKey(o); }
isEmpty
public boolean isEmpty() { //调用map的isEmpty方法 return map.isEmpty(); }
迭代
public Iterator<E> iterator() {
//由于不须要value,因此只是调用了keySet的iterator
return map.keySet().iterator(); }
分析了一下,其实最终的底层实现都是在调用HashMap的方法,因此了解了HashMap的源码以后,HashSet其实就会比较简单了
总结
HashSet是非线程安全的,容许插入空元素HashSet不容许重复元素HashSet的Key须要复写hashcode跟equals方法