Set接口是Collection的子接口,Set中的元素是唯一的。Set不包含满足e1.equals(e2)的元素,并且最多包含一个null元素。
HashSet是Set接口最常用的一种实现,但它不保证元素的迭代顺序,并允许使用null元素。
HashSet的底层是基于HashMap实现的。
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<E,Object>();
}
这个就是HashSet的内部实现,这也就是API中HashSet的默认无参的构造函数,构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
在HashSet类的内部会看到两个私有的变量:
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
一个是HashMap类型的变量,一个是静态常量的对象类型变量。在最常用的add()方法中会看到这两个变量的应用:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
也就是说往Set类型的集合里加值,其实是往Map类型集合中键的位置加值,而在Map集合中值的位置永远都是一个不可改变的Object类型的对象。而map类型集合中键的值是要求唯一并且不可重复的。因此这也可以间接的说明为什么Set类型的集合中值只能是唯一且不可重复的。
public class HashSetTest<E> {
private HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSetTest() {
map = new HashMap<E,Object>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSetTest<String> hashSetTest=new HashSetTest<String>();
System.out.println("result1:"+hashSetTest.add("one"));
System.out.println("result2:"+hashSetTest.add("two"));
System.out.println("result3:"+hashSetTest.add("one"));
}
}
结果会是:
result1:true
result2:true
result3:false
这里简洁的剖析了下HashSet的源码实现。