对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码:
(1)使用HashMap的key保存HashSet的元素。
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
// 使用 HashMap 的 key 保存 HashSet 中所有元素**************************
private transient HashMap<E,Object> map;
// 定义一个虚拟的 Object 对象作为 HashMap 的 value ********************
private static final Object PRESENT = new Object();
...
// 初始化 HashSet,底层会初始化一个 HashMap
public HashSet()
{
map = new HashMap<E,Object>();
}
// 以指定的 initialCapacity、loadFactor 创建 HashSet
// 其实就是以相应的参数创建 HashMap
public HashSet(int initialCapacity, float loadFactor)
{
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity)
{
map = new HashMap<E,Object>(initialCapacity);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy)
{
map = new LinkedHashMap<E,Object>(initialCapacity
, loadFactor);
}
// 调用 map 的 keySet 来返回所有的 key **************
public Iterator<E> iterator()
{
return map.keySet().iterator();
}
// 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 Set 里元素的个数
public int size()
{
return map.size();
}
// 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,
// 当 HashMap 为空时,对应的 HashSet 也为空
public boolean isEmpty()
{
return map.isEmpty();
}
// 调用 HashMap 的 containsKey 判断是否包含指定 key
//HashSet 的所有元素就是通过 HashMap 的 key 来保存的
public boolean contains(Object o)
{
return map.containsKey(o);
}
// 将指定元素放入 HashSet 中,也就是将该元素作为 key 放入 HashMap
public boolean add(E e)
{
return map.put(e, PRESENT) == null;
}
// 调用 HashMap 的 remove 方法删除指定 Entry,也就删除了 HashSet 中对应的元素
public boolean remove(Object o)
{
return map.remove(o)==PRESENT;
}
// 调用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素
public void clear()
{
map.clear();
}
...
}
由上面源程序可以看出,HashSet 的实现其实非常简单,它只是封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。
2.因为HashSet底层是HashMap这也就说明了以下HashSet的几个特性:
(1)HashSet是无序的
(2)HashSet是不同步的
(3)元素集合可以为null,但只能一个为空,以及set是不重复的集合。
(4)hashcode是比对key值,value不用比较。
是否相等:hashcode相等---同一个bucket,且equals(key)相等。
3.LinkedHashSet
HashSet的子类,也是通过hashcode定位bucket,然后通过equals判断是否相等,只是多了一个链表来记录添加元素的顺序。所以输出元素的顺序总是与添加顺序一致。
4.TreeSet(所有元素必须实现Comparable接口,底层红黑树)
是SortedSet接口的实现类,按照升序排序。也可以采用定制排序 Comparator 的comparator()。
自然排序,默认的就是Comparator里面的compareTo()方法进行比较。
TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序
或者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。
TreeSet为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销
是否相等:compareTo(o1,o2)返回值是否为0,不考虑equals。所以即使equals为true,但compareTo返回值不是0,那么两个对象也不相等。
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
// NavigableMap对象*********基于TreeMap实现的
private transient NavigableMap<E,Object> m;
// TreeSet是通过TreeMap实现的,
// PRESENT是键-值对中的值。
private static final Object PRESENT = new Object();
// 不带参数的构造函数。创建一个空的TreeMap
public TreeSet() {
this(new TreeMap<E,Object>());
}
// 将TreeMap赋值给 "NavigableMap对象m"
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
// 带比较器的构造函数。
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<E,Object>(comparator));
}