一、HashSet概述
HashSet是Set接口的一个实现类。
??HashSet继承了AbstractSet,实现了Set,它是一个集合,支持相关的添加、删除、修改、遍历等功能。
??HashSet实现了Cloneable接口,能被克隆。
??HashSet实现了Serializable接口,这意味着Serializable支持序列化,能通过序列化去传输。
??HashSet底层是由HashMap实现的,其功能基本都是调用HashMap的相关接口来实现的。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
??HashSet是不同步的。
二、HashSet源码
??HashSet源码中最先出现的是定义了一个变量:
1 | transient HashMap<E, HashSet<E>> backingMap; |
??从这个变量的声明可以看出,HashSet实际上是由HashMap实现的,并且不用序列化。
2.1 构造方法
??HashSet有4个公外部使用的构造方法:无参构造方法、指定初始容量的构造方法、指定初始容量和增长因子的构造方法和参数为collection的构造方法。在构造方法中,默认的初始容量是16,加载因子是 0.75。
??public HashSet(),构造一个空的HashMap,源码如下:
1 2 3 4 5 6 | public HashSet() { this(new HashMap<E, HashSet<E>>()); } HashSet(HashMap<E, HashSet<E>> backingMap) { this.backingMap = backingMap; } |
??public HashSet(int capacity),创建一个具有初始容量的HashMap,源码如下:
1 2 3 | public HashSet(int capacity) { this(new HashMap<E, HashSet<E>>(capacity)); } |
??public HashSet(int capacity, float loadFactor),创建初始容量和增长因子的HashMap,源码如下:
1 2 3 | public HashSet(int capacity, float loadFactor) { this(new HashMap<E, HashSet<E>>(capacity, loadFactor)); } |
??public HashSet(Collection extends E> collection),参数为collection的构造方法,源码如下:
1 2 3 4 5 6 7 | public HashSet(Collection<? extends E> collection) { this(new HashMap<E, HashSet<E>>(collection.size() < 6 ? 11 : collection .size() * 2)); for (E e : collection) { add(e); } } |
??从上面代码可以看出,该方法的实现是:先创建一个具有初始容量的HashMap,这个初始容量取决于collection的元素数量;然后将collection中的元素挨个添加到HashMap中。
2.2 其他方法
??public boolean add(E object),添加元素,源码如下:
1 2 3 | public boolean add(E object) { return backingMap.put(object, this) == null; } |
??public void clear(),清空HashSet,源码如下:
1 2 3 | public void clear() { backingMap.clear(); } |
??public Object clone(),返回HashSet的浅克隆对象。
??public boolean contains(Object object),判断是否包含某个元素,源码如下:
1 2 3 | public boolean contains(Object object) { return backingMap.containsKey(object); } |
??从这个方法能看出HashSet中的对象是保存在HashMap的key中的,HashM的value保存的是常量。
??public boolean isEmpty(),判断当前HashSet是否为空,源码如下:
1 2 3 | public boolean isEmpty() { return backingMap.isEmpty(); } |
??public Iterator iterator(),返回迭代器,源码如下:
1 2 3 | public Iterator<E> iterator() { return backingMap.keySet().iterator(); } |
??public boolean remove(Object object),移除元素,源码如下:
1 2 3 | public boolean remove(Object object) { return backingMap.remove(object) != null; } |
??public int size(),返回HashSet中的元素数量,源码如下:
1 2 3 | public int size() { return backingMap.size(); } |
??private void writeObject(ObjectOutputStream stream),将HashSet中的数据写入到输入流中,源码如下:
1 2 3 4 5 6 7 8 9 | private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(backingMap.table.length); stream.writeFloat(HashMap.DEFAULT_LOAD_FACTOR); stream.writeInt(size()); for (E e : this) { stream.writeObject(e); } } |
??从上面代码可以看出,该方法的实现是:先写容量,再写增长因子,最后写具体的数据。
??private void readObject(ObjectInputStream stream),从输入流中读取数据,源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 | private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int length = stream.readInt(); float loadFactor = stream.readFloat(); backingMap = createBackingMap(length, loadFactor); int elementCount = stream.readInt(); for (int i = elementCount; --i >= 0;) { E key = (E) stream.readObject(); backingMap.put(key, this); } } |
??从上面代码可以看出,该方法的实现是:先读取容量,再读增长因子,最后读取数据。
三、HashSet遍历
??HashSet常用的遍历方法有2种:Iterator遍历和foreach循环。
??测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Set<String> hashSetTest = new HashSet<String>(); hashSetTest.add("看书"); hashSetTest.add("看电影"); hashSetTest.add("看电视剧"); hashSetTest.add("看综艺"); System.out.println("迭代器遍历结果:"); Iterator<String> it = hashSetTest.iterator(); while (it.hasNext()) { System.out.println(it.next()); } System.out.println("foreach循环遍历结果:"); for (String str : hashSetTest) { System.out.println(str); } |
??测试结果如下:
迭代器遍历结果:
看书
看综艺
看电影
看电视剧
foreach循环遍历结果:
看书
看综艺
看电影
看电视剧