Welcome 微信登录

首页 / 软件开发 / JAVA / Java集合学习(十六) HashSet详细介绍(源码解析)和使用示例

Java集合学习(十六) HashSet详细介绍(源码解析)和使用示例2014-08-03 cnblogs skywang12345这一章,我们对HashSet进行学习。
我们先对HashSet有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashSet。

第1部分 HashSet介绍

HashSet 简介

HashSet 是一个没有重复元素的集合。
它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。
HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:

Set s = Collections.synchronizedSet(new HashSet(...));

HashSet通过iterator()返回的迭代器是fail-fast的。

HashSet的继承关系如下:

java.lang.Objectjava.util.AbstractCollection<E>java.util.AbstractSet<E>java.util.HashSet<E> public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, java.io.Serializable { }
HashSet与Map关系如下图:

HashSet的构造函数

// 默认构造函数public HashSet()// 带集合的构造函数public HashSet(Collection<? extends E> c)// 指定HashSet初始容量和加载因子的构造函数public HashSet(int initialCapacity, float loadFactor)// 指定HashSet初始容量的构造函数public HashSet(int initialCapacity)// 指定HashSet初始容量和加载因子的构造函数,dummy没有任何作用HashSet(int initialCapacity, float loadFactor, boolean dummy)
HashSet的主要API

boolean add(E object)voidclear()Objectclone()boolean contains(Object object)boolean isEmpty()Iterator<E> iterator()boolean remove(Object object)int size()
第2部分 HashSet源码解析

为了更了解HashSet的原理,下面对HashSet源码代码作出分析。

package java.util; public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, java.io.Serializable{static final long serialVersionUID = -5024744406713321676L; // HashSet是通过map(HashMap对象)保存内容的private transient HashMap<E,Object> map; // PRESENT是向map中插入key-value对应的value// 因为HashSet中只需要用到key,而HashMap是key-value键值对;// 所以,向map中添加键值对时,键值对的值固定是PRESENTprivate static final Object PRESENT = new Object(); // 默认构造函数public HashSet() {// 调用HashMap的默认构造函数,创建mapmap = new HashMap<E,Object>();} // 带集合的构造函数public HashSet(Collection<? extends E> c) {// 创建map。// 为什么要调用Math.max((int) (c.size()/.75f) + 1, 16),从 (c.size()/.75f) + 1 和 16 中选择一个比较大的树呢?// 首先,说明(c.size()/.75f) + 1// 因为从HashMap的效率(时间成本和空间成本)考虑,HashMap的加载因子是0.75。// 当HashMap的“阈值”(阈值=HashMap总的大小*加载因子) < “HashMap实际大小”时,// 就需要将HashMap的容量翻倍。// 所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。// 接下来,说明为什么是 16 。// HashMap的总的大小,必须是2的指数倍。若创建HashMap时,指定的大小不是2的指数倍;// HashMap的构造函数中也会重新计算,找出比“指定大小”大的最小的2的指数倍的数。// 所以,这里指定为16是从性能考虑。避免重复计算。map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));// 将集合(c)中的全部元素添加到HashSet中addAll(c);} // 指定HashSet初始容量和加载因子的构造函数public HashSet(int initialCapacity, float loadFactor) {map = new HashMap<E,Object>(initialCapacity, loadFactor);} // 指定HashSet初始容量的构造函数public HashSet(int initialCapacity) {map = new HashMap<E,Object>(initialCapacity);} HashSet(int initialCapacity, float loadFactor, boolean dummy) {map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);} // 返回HashSet的迭代器public Iterator<E> iterator() {// 实际上返回的是HashMap的“key集合的迭代器”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);} // 将元素(e)添加到HashSet中public boolean add(E e) {return map.put(e, PRESENT)==null;} // 删除HashSet中的元素(o)public boolean remove(Object o) {return map.remove(o)==PRESENT;} public void clear() {map.clear();} // 克隆一个HashSet,并返回Object对象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();}} // java.io.Serializable的写入函数// 将HashSet的“总的容量,加载因子,实际容量,所有的元素”都写入到输出流中private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {// Write out any hidden serialization magics.defaultWriteObject(); // Write out HashMap capacity and load factors.writeInt(map.capacity());s.writeFloat(map.loadFactor()); // Write out sizes.writeInt(map.size()); // Write out all elements in the proper order.for (Iterator i=map.keySet().iterator(); i.hasNext(); )s.writeObject(i.next());}// java.io.Serializable的读取函数// 将HashSet的“总的容量,加载因子,实际容量,所有的元素”依次读出private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// Read in any hidden serialization magics.defaultReadObject(); // Read in HashMap capacity and load factor and create backing HashMapint capacity = s.readInt();float loadFactor = s.readFloat();map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); // Read in sizeint size = s.readInt(); // Read in all elements in the proper order.for (int i=0; i<size; i++) {E e = (E) s.readObject();map.put(e, PRESENT);}}}
说明: