写在前面的话
本文针对的是Java1.6进行的源码分析,与其他版本可能存在差异。
概述
HashSet和LinkedHashSet分别是用HashMap和LinkedHashMap来实现的,它们是把数据作为Key值存入到HashMap或LinkedHashMap中。因此Set都不允许有重复的值,且HashSet是无序的,LinkedHashSet是有序的。之前,已经介绍过HashMap和LinkedHashMap的相关内容了,可以分别参考Java容器HashMap源代码解析 和 Java容器LinkedHashMap源代码解析 。
源代码解析
LinkedHashSet是继承自HashSet的,因此,我们先看HashSet的源代码。
属性和构造方法
//用于存放数据
private transient HashMap<E,Object> map;
//存放到HashMap中的值
private static final Object PRESENT = new Object();
//默认构造方法,新建一个容量为16,加载因子为0.75的HashMap
public HashSet() {
map = new HashMap<E,Object>();
}
//带有collection参数的构造方法
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
//给定容量和加载因子的构造方法
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
//给定容量的构造方法
public HashSet(int initialCapacity) {
map = new HashMap<E,Object>(initialCapacity);
}
//多了dummy参数,在LinkedHashSet会用这个构造方法
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}
带有collection参数的构造函数用到了addAll方法,在HashSet中并没有重写这个方法,因此这里直接用的AbstractCollection中的该方法。代码如下:
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
Iterator<? extends E> e = c.iterator();
//遍历collection迭代器
while (e.hasNext()) {
//调用相应容器的add方法
if (add(e.next()))
modified = true;
}
return modified;
}
在HashSet中定义了两个属性,map用于存放数据,PRESENT 是往map中存放数据时,默认的value值。它有五个构造方法,前四个都是调用HashMap的对应构造方法来初始化map。最后一个多了dummy函数,是提供给LinkedHashSet用的,LinkedHashSet通过调用LinkedHashMap的构造方法来初始化map。
其它方法
//迭代器,因为HashSet相当于对HashMap的key进行遍历,所以直接返回
//map中keySet的迭代器即可。因此HashSet也是无序的。
public Iterator<E> iterator() {
return map.keySet().iterator();
}
//返回大小,直接返回map的大小即可
public int size() {
return map.size();
}
//判断是否为空,也就是判断map是否为空
public boolean isEmpty() {
return map.isEmpty();
}
//判断是否包含某个值,直接调用map的containsKey方法
public boolean contains(Object o) {
return map.containsKey(o);
}
//添加元素,直接调用map的put方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//删除元素,直接调用map的remove方法
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
//情空Set,直接调用map的清空方法
public void clear() {
map.clear();
}
//拷贝set
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();
}
}
HashSet提供的这些方法都是通过调用HashMap的对应方法来实现的,在这里不再详述,如果不熟悉HashMap中这些方法的具体实现,请参考文章开头提到的两篇博文。
LinkedHashSet构造方法
介绍完了HashSet,我们再看一下LinkedHashSet的具体实现。LinkedHashSet是继承自HashSet的,它本身只提供了四个构造方法,都是通过调用HashSet最后一个构造函数来实现的。代码如下:
//给定map容量和加载因子的构造方法
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
//给定容量的构造方法,加载因子默认用0.75
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
//默认构造方法,容量用默认值16,加载因子用默认值0.75
public LinkedHashSet() {
super(16, .75f, true);
}
//带有collection参数的构造方法
public LinkedHashSet(Collection<? extends E> c) {
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}