HashSet源码分析
今天来分析HashSet源码:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
打开HashSet源码发现,HashSet是继承自AbstractSet,实现了Clonable和Serializable类,而我们知道Set接口是继承自Collection的。
HashSet内部属性:
static final long serialVersionUID = -5024744406713321676L;
//通过HashMap保存对象
private transient HashMap<E,Object> map;
//定义一个虚拟的Object PRESENT是向map中插入key-value对应的value
//该map的key就是HashSet要存放的数据,而HashMap不允许key重复,所以HashSet不允许值重复
private static final Object PRESENT = new Object();
我们发现,HashSet内部原来是基于HashMap实现的,因此我们有了信心阅读下去
HashSet主要方法:(我们的顺序是根据源码行的顺序阅读的)
HashSet的无参数构造函数:
public HashSet() {
map = new HashMap<>();
}
默认的HashSet构造函数内部调用了默认的HashMap构造函数,实际只是实例化了hashMap对象,而我们知道默认的HashMap构造函数默认的大小是16,默认的加载因子是0.75
HashSet一个参数构造函数:
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
传入指定的初始化大小参数,同样把初始化大小传给HashMap,实现初始化。
HashSet两个参数构造函数:
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
Hash添加元素add(E e)方法:
//添加一个元素,如果该元素已经存在,则返回true,如果不存在,则返回false
public boolean add(E e) {
//往map中添加元素,返回null,说明是第一个往map中添加该key
return map.put(e, PRESENT)==null;
//把PRESENT作为value,所以map中的所有key对应的value都相等
}
- 往hashset中添加元素,实际是往map成员变量里面添加对应的key和value
- map中的key实际就是要添加的元素,value是一个固定对象PRESENT
- 当第一次往map中添加key时,添加成功返回null,所以当第一次往HashSet中添加元素时,会返回true
- 由于HashMap中的key不能重复,所以HashSet不能存储重复元素
- 因为HashMap存储元素是Hash散列存储,所以HashSet中的元素也是无序的
删除元素方法remove(Object o)
//删除指定的元素,删除成功返回true
public boolean remove(Object o) {
//实际是删除map中的一个对象
return map.remove(o)==PRESENT;
}
- 当hashset删除一个元素时,实际是操作map删除对应的元素
- 当删除map中一个不存在的对象时,会返回null,所以这里当返回PRESENT时,说明之前HashSet往map中添加过对应的元素,因此,当remove(o)返回true时,说明之前已经存在该元素,并且成功删除,当返回false时,说明之前并没有添加过该对象。
hashet也没有提供get方法,可以通过Iterator迭代器获取数据
public Iterator<E> iterator() {
return map.keySet().iterator();
}
总结:
- HashSet内部使用HashMap的key来存储元素,来保证元素不重复
- HashSet是无序的,因为HashMap是散列存储,所以无序
- HashSet中允许有一个null元素,以为HashMap允许key为null
- HashSet非线程安全
- HashSet没有get方法,获取方法是通过HashMap的迭代器实现