基本特性
HashSet的是依靠组合一个HashMap实现的。然后讲大部分任务都委托给HashMap完成。
当然,HashSet不保证迭代顺序与添加顺序相同,而且也不保证其顺序不变。允许空元素。
对于其迭代器的迭代效率正比于(HashSet的内元素和HashSet的桶数量之和),因此如果对迭代效率要求比较高,就不要使用过大的初始大小。(这部分从HashSet本身的代码看不出来,今后分析HashMap的时候再说)
对于同步特性,当然HashMap本身的不同步的,所以HashSet本身也不是线程安全的,所以如果要保证线程安全至少要用Set s = Collections.synchronizedSet(new HashSet(...));
,当然还要注意使用同步包装器只是限制每次只能一个线程访问。
对于迭代器使用的HashMap.keySet.iterator实现的,fail-fast迭代器。
HashSet本身的内容很少。
如果将任务委托给HashMap
之前说过HashSet内置了一个HashMap的域变量,然后将所有的操作都委托给HashMap,这里的实现实际上就是先定义一个类静态变量的哑节点,就是PRESENT。然后将其作为HashMAP的值,然后将Set作为Key。这样就可以讲Set的认为委托给HashMap执行。
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
初始化
其实构造器也没有特别的地方,基本上都是把所有的认为委托给HashMap,但是HashSet有两种实现,分别是是HashSet和LinkedHashSet,也分别对应了HashMap和LinkedHashMap的实现
为了区别这两者HashSet中实现了一个默认访问权限的构造器,然后让LinkedHashSet继承HashSet。如果需要使用LinkedHashSet则只要在LinkedHashSet中使用构造器然后在尾部加上true(其实加上false也可以)就可以使用LinkedHashMap来实现。
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
LinkedHashSet
基本特性
这里与HashSet不同使用了一个双端队列实现HashSet。从而实现了有序的排列。LinkedHashSet维护的是插入顺序,而且不受重复插入的影响,也就是仅仅以第一次插入操作为准。
客户端的散列实没有特殊指定,通常使用HashSet的散列顺序,而使用TreeSet则会有稍高的代价。这样当如果复制元素时依然会保持原先的顺序,通常符合使用者习惯。这句话其实我不是很理解。
LinkedHashSet是HashSet的子类,允许空元素,插入包含删除操作有常数时间复杂度,但是时间会稍多于HashSet因为需要维护双端队列。
当然LinkedHashSet的性能会收到初始大小和装填因子的影响,但是和HashSet有些不同,HashSet元素迭代性能消耗并不受容量的影响,因此初始大小的惩罚没有HashSet这么大。
实现
实现上实际上没有任何特殊的地方。只是重写了构造器。还利用了HashSet的构造器。