Java集合框架知识点总结(四)
Java集合前三篇总结了List和Map的常用实现类,这篇讲讲Set,以最常用的HashSet为例对其源代码进行详细讲解。前一篇对于HashMap的讲解比较难,也是因为在众多集合中Map的存储比较复杂,而且在JDK 1.8中变化较大,Chuck之前也只读过JDK 1.7的HashMap源码,对于JDK 1.8的变化也是边读边总结。
如果对HashMap的理解比较透彻,那么这篇HashSet的详解读起来会很轻松。为什么呢?看了源码就知道了。
类的定义,感觉Java集合类定义完全都是同一种套路:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
Set接口依旧是提供了Set集合类的某种规范,定义了add(),remove(),contains()等基本方法,而AbstractSet抽象类实现了Set接口的equals(),hashCode()和removeAll()方法,其他方法都在各个具体子类中实现。同时可以知道HashSet支持克隆和序列化。
为什么说理解了HashMap,HashSet也就理解了呢?从HashSet的两大属性说起:
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
明白了吧,HashSet实际就是借助HashMap来实现存储的,所以相较于HashMap两千多行的源码量,HashSet只有少得可怜的三百多行。
public HashSet() {
map = new HashMap<>();
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
从构造方法看出构造HashSet实际就是构建其中的HashMap,HashMap有几种构造器相应的HashSet也就有集中构造器。
Set是以对象进行存储的,Map是以键值对进行存储,Set在存储时需要解决这一差异,需要从put操作讲起:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
由前文提到的两个属性,PRESENT是一个Object对象,在存储元素时通过将元素作为Key值,Value为一个Object对象的方式进行存储。这就说明了为什么Set不能存储重复元素,因为Key会冲突。
Set其实讲到这里已经没什么可讲的了,因为HashSet中除了几个克隆和序列化相关的方法外,其余的方法完全是在调用HashMap实例的各个方法。
Java集合框架基础知识到这就结束了,希望能够对各位有帮助,重点掌握HashMap和List,能够讲明白ArrayList和LinkedList的区别,通过讲解源码的方式说明它们的不同。HashSet在面试中没有被问到,但Set作为Java集合三大组成部分之一,应该被理解和掌握。之前在HashMap讲解中提到的HashMap并发死锁问题,将放到讲并发的专题部分讲解。