文章目录:
1.开篇
前面三篇文章分别说到了 List 接口中的常用几个实现类:ArrayList、LinkedList、Vector。
而Java集合体系中,List继承了Collection接口,Collection接口又继承了Iterable接口,而在Collection接口的主要的子接口中还有一个兄弟:Set。
这篇文章主要来讲讲Set接口的主要实现类 HashSet、LinkedHashSet的实现原理。那么我们看过部分源码的都知道Set集合的底层实际上就是相应的Map,所以说Set还不如说Map呢,所以这篇文章就简单的对Set集合的源码进行一个分析吧。
2.HashSet中的属性
· HashSet的底层其实就是HashMap(哈希表数据结构),new HashSet的时候实际上就是new了一个HashMap。
· HashSet集合元素的特点:无序、不可重复、没有下标。
· HashSet集合的默认初始化容量是16,默认加载因子是 .75。(HashMap)
//底层使用 HashMap 来保存 HashSet 中所有元素 private transient HashMap<E,Object> map; //定义一个虚拟的 Object 对象作为 HashMap 的 value,将此对象定义为 static final private static final Object PRESENT = new Object();
3.HashSet中的方法
3.1 构造方法一
//默认的无参构造器,构造一个空的 HashSet //实际底层会初始化一个空的 HashMap,并使用默认初始容量为 16 和加载因子 0.75 public HashSet() { map = new HashMap<>(); }
3.2 构造方法二
//构造一个包含指定 collection 中的元素的新 set //实际底层使用默认的加载因子 0.75 和足以包含指定 collection 中所有元素的初始容量来创建一个 HashMap //c 其中的元素将存放在此 set 中的 collection public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); }
3.3 构造方法三
//以指定的 initialCapacity 和 loadFactor 构造一个空的 HashSet //实际底层以相应的参数构造一个空的 HashMap //initialCapacity 初始容量,loadFactor 加载因子 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); }
3.4 构造方法四
//以指定的 initialCapacity 构造一个空的 HashSet //实际底层以相应的参数及加载因子 loadFactor 为 0.75 构造一个空的 HashMap public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); }
3.5 构造方法五
//以指定的 initialCapacity 和 loadFactor 构造一个新的空链接哈希集合 //此构造函数为包访问权限,不对外公开,实际只是是对 LinkedHashSet 的支持 //实际底层会以指定的参数构造一个空 LinkedHashMap 实例来实现 HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
3.6 迭代器Iterator方法
//返回对此 set 中元素进行迭代的迭代器。返回元素的顺序并不是特定的。 //底层实际调用底层 HashMap 的 keySet 来返回所有的 key //可见 HashSet 中的元素,只是存放在了底层 HashMap 的 key 上, //value 使用一个 static final 的 Object 对象标识 public Iterator<E> iterator() { return map.keySet().iterator(); }
3.7 size方法
//返回此 set 中的元素的数量(set 的容量) //底层实际调用 HashMap 的 size()方法返回 Entry 的数量,就得到该 Set 中元素的个数 public int size() { return map.size(); }
3.8 isEmpty方法
//如果此 set 不包含任何元素,则返回 true //底层实际调用 HashMap 的 isEmpty()判断该 HashSet 是否为空 public boolean isEmpty() { return map.isEmpty(); }
3.9 contains方法
//如果此 set 包含指定元素,则返回 true //更确切地讲,当且仅当此 set 包含一个满足(o==null ? e==null : o.equals(e))的 e 元素时,返回 true //底层实际调用 HashMap 的 containsKey 判断是否包含指定 key public boolean contains(Object o) { return map.containsKey(o); }
3.10 add方法
//如果此 set 中尚未包含指定元素,则添加指定元素 //更确切地讲,如果此 set 没有包含满足(e==null ? e2==null : e.equals(e2))的元素 e2,则向此 set 添加指定的元素 e //如果此 set 已包含该元素,则该调用不更改 set 并返回 false //底层实际将将该元素作为 key 放入 HashMap。 //由于 HashMap 的 put()方法添加 key-value 对时,当新放入 HashMap 的 Entry 中 key 与集合中原有 Entry 的 key 相同(hashCode()返回值相等,通过 equals 比较也返回 true) //新添加的 Entry 的 value 会将覆盖原来 Entry 的 value,但 key 不会有任何改变, //因此如果向 HashSet 中添加一个已经存在的元素时,新添加的集合元素将不会被放入 HashMap 中,原来的元素也不会有任何改变,这也就满足了 Set 中元素不重复的特性 public boolean add(E e) { return map.put(e, PRESENT)==null; }
3.11 remove方法
//如果指定元素存在于此 set 中,则将其移除 //更确切地讲,如果此 set 包含一个满足(o==null ? e==null : o.equals(e))的元素e,则将其移除。如果此 set 已包含该元素,则返回 true //(或者:如果此 set 因调用而发生更改,则返回 true)。(一旦调用返回,则此 set 不再包含该元素)。 //底层实际调用 HashMap 的 remove 方法删除指定 Entry public boolean remove(Object o) { return map.remove(o)==PRESENT; }
3.12 clear方法
//从此 set 中移除所有元素。此调用返回后,该 set 将为空 //底层实际调用 HashMap 的 clear 方法清空 Entry 中所有元素 public void clear() { map.clear(); }
4.LinkedHashSet中的方法
LinkedHashSet 是 HashSet 的一个子类。LinkedHashSet 是具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与HashSet 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可为插入顺序或是访问顺序。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希 Set,而其中至少一个线程修改了该 Set,则它必须保持外部同步。
对于 LinkedHashSet 而言,它继承与 HashSet、又基于 LinkedHashMap 来实现的。LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法操作上又与 HashSet 相同,因此 LinkedHashSet 的实现上非常简单,只提供了四个构造方法,并通过传递一个标识参数dummy,调用父类的构造器,底层构造一个 LinkedHashMap来实现,在相关操作上与父类 HashSet 的操作相同,直接调用父类 HashSet 的方法即可。
//构造一个带有指定初始容量和加载因子的新空链接哈希 set //底层会调用父类的构造方法,构造一个有指定初始容量和加载因子的 LinkedHashMap 实例 initialCapacity 初始容量,loadFactor 加载因子 public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } //构造一个带指定初始容量和默认加载因子 0.75 的新空链接哈希 set //底层会调用父类的构造方法,构造一个带指定初始容量和默认加载因子 0.75 的 LinkedHashMap 实例 public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } //构造一个带默认初始容量 16 和加载因子 0.75 的新空链接哈希 set //底层会调用父类的构造方法,构造一个带默认初始容量 16 和加载因子 0.75 的 LinkedHashMap 实例 public LinkedHashSet() { super(16, .75f, true); } //构造一个与指定 collection 中的元素相同的新链接哈希 set //底层会调用父类的构造方法,构造一个足以包含指定 collection中所有元素的初始容量和加载因子为 0.75 的 LinkedHashMap 实例 //c 其中的元素将存放在此 set 中的 collection public LinkedHashSet(Collection<? extends E> c) { super(Math.max(2*c.size(), 11), .75f, true); addAll(c); }