Set源码解析
首先我们看看在JDK中Set端口的源码:
package asdf;
import java.util.*;
import java.util.Collection;
/**
* Created by ping.miao on 2015/7/29.
* set源码分析
*/
public interface Set<E> extends Collection<E> {
int size();
/**
* @return Set包含元素的个数
*/
boolean isEmpty();
/**
* @return 判断Set是否为空,为空返回true,不为空返回false
*/
boolean contains(Object o);
/**
* @return 判断set是否包含元素与o相等,假如o == null,那么只需要判断set中是否有元素为null
* 假如有返回true,没有返回false,假如 o != null,判断set中是否有元素与o相等,有返回true,
* 没有返回false
*/
Iterator<E> iterator();
/**
* 返回包含set所有元素的Iterator
*/
Object[] toArray();
/**
* 返回包含set所有元素的array
*/
<T> T[] toArray(T[] a);
/**
* 返回一个包含set元素的指定类型的数组
*/
boolean add(E e);
/**
* 插入元素,假如当前set中存在元素与e相等,那么保持原set不改变,返回false,
* 否则插入元素,并返回true
*/
boolean remove(Object o);
/**
* remove类似于这样的元素(o == null? e == null : o.equals(e)),并返回true
*/
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean retainAll(Collection<?> c);
boolean removeAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
}
从源码可以看出Set端口是Collection端口的子端口,上面的代码中已经做了详细的注解,这里不再赘述Set中各个方法是用来干什么的。
接下来我们看看HashSet的源码:
HashSet源码:
首先看看HashSet到底是一个什么样的类,它继承了哪些类。
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
由上面的HashSet的头部可以看到,HashSet是AbstractSet的子类、Set的子接口,并且实现了其他额外的一些东西,Cloneable和Serializable。我们先看看AbstractSe中的部分源码。
package asdf;
import java.util.*;
import java.util.Collection;
import java.util.Set;
/**
* Created by ping.miao on 2015/7/30.
*/
public abstract class AbstractSet<E> extends AbstractCollection<E> implements java.util.Set<E> {
protected AbstractSet() {
}
/**
* 构造方法
*/
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
java.util.Collection c = (Collection) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
/**
* 比较Object o与set是否相等,o可以是单个元素,也可以是一个Set。o是单个元素的时候
* 假如set中存在与o相等的元素返回true,否则返回false。o是Set的时候,假若它与当前
* set长度相同,并且每个元素相等,返回true,否则返回false。
*/
public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}
/**
* 得到set的hashcode。假如当前值为null的时候,它的hashcode是0,最后返回的是set中
* 所有元素的hashcode的总和。
*/
public boolean removeAll(Collection<?> c) {
boolean modified = false;
if (size() > c.size()) {
for (Iterator<?> i = c.iterator(); i.hasNext(); )
modified |= remove(i.next());
} else {
for (Iterator<?> i = iterator(); i.hasNext(); ) {
if (c.contains(i.next())) {
i.remove();
modified = true;
}
}
}
return modified;
}
/**
* 删除参数中给出的set中的所有元素,删除成功返回true,失败返回false
*/
}
底层实现:
/**
* 底层使用HashMap来保存HashSet中的所有元素
*/
private transient HashMap<E,Object> map;
/**
* 定义一个虚拟的Object对象作为HashMap的value
*/
private static final Object PRESENT = new Object();
构造方法:
/**
* 无参数构造方法,构造一个空的HashSet
* 实际的底层会初始化一个空的HashMap,并且使用默认容量16,填装因子0.75
*/
public HashSet() {
map = new HashMap<>();
}
/**
* 构造一个包含指定的Collection中的元素的set
* 实际底层使用默认的填装因子0.75和可以包含Collection中所有元素的初始容量来
* 创建一个新的HashMap
*
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* 以指定的initialCapacity和loadFactor创建一个空的HashSet
* @param initialCapacity HashSet大小初始值
* @param loadFactor 填装因子
*
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
/**
*
* @param initialCapacity HashSet大小初始值,构造一个空的HashSet
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
*
* @param initialCapacity HashSet初始大小
* @param loadFactor 填装因子
* @param dummy 标记
* 返回一个空的LinkedHashSet
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
数据插入删除:
/**
*
* @param e 待插入元素,若没有满足(o == null ? e == null : o.equals(e))
* 的元素则插入
* @return 插入成功返回true,插入失败返回false
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
*
* @param o 若o存在则删除
* @return true or false,删除成功返回true,删除失败返回false
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}<strong>
</strong>
数据读取和遍历:
/**
*
* @return 返回Set的所有元素,并且这些元素是没有特定顺序的
* 底层调用HashMap的keySet返回所有的key
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
看到这里大家估计很困惑,set里面的元素是不能重复的,而且值为null的元素只能有一个,这些是怎么来实现的呢。
首先我们看看HashSet的构造方法:
你会发现HashSet调用了一个方法addAll,接下来我们去addAll方法看看它是怎么实现的。
addAll是AbstractCollection的方法,看到上面的源码,我们似乎应该去看看add方法是怎么实现的,点到add方法,发现它只是抛出一个异常,看到这里,你可能会说,这个没有实现啊,对,这个方法确实没有展现给我们它到底是怎么实现的。但是它总是用某个方法实现了,我们去看看HashSet的add方法是怎么来实现的:
HashSet调用了HashMap的put方法,接下来我们去看看HashMap的put方法的源码是怎么实现的:
可以看出是通过对key产生的hashcode和判断是否set中已有值与它相等来判断set中是否有重复的值的。接下来我们看看hashcode是怎么产生的: