下面就是HashSet的继承关系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bMUfHjgu-1593755190238)(https://raw.githubusercontent.com/iszhonghu/Picture-bed/master/img/20200702171047.png)]
当我们去看HashSet的构造方法的时候我们会发现直接new了一个HashMap,并且赋给了map属性。
即HashSet就是在HashMap上面套了个壳,方法也比较简单,每个方法其实都是对应的操作map。
因为HashSet是继承了Set,所以就不能有重复项。我们大多数情况下使用HashSet也是因为它有去重的功能。
要实现去重,HashSet从add方法就开始了
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
从这里我们可以看出,HashSet的add方法调用了HashMap的put方法,但是put进去的是一个键值对,而HashSet存的不是键值对,是一个泛型。
所以说HashSet把你要存放的值当做key,而对应的value是一个final的Object对象,只起到一个占位作用。这个时候由于HashMap不允许key重复所以正好被HashSet拿来使用用来保证其不重复。
由于HashMap不是线程安全的,所以HashSet也不是线程安全的,所以在多线程,高并发的情况下慎用。
而且HashSet没有像Hashmap那样的多线程版本,如果想用多线程模式就使用如下的:
Set<String> set = Collections.synchronizedSet(new HashSet<String>());
或者使用ConcurrentHashMap的一个实现了Set接口的静态内部类。
类似的LinkedHashSet也是一个套壳的LinkedHashMap,对比HashSet它的特点就是保证数据有序,插入的时候是什么顺序,遍历的时候就是什么顺序
public LinkedHashSet() {
super(16, .75f, true);
}
由于LinkHashSet继承自HashSet所以其实是调用了HashSet的三个参数的构造函数
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
这次不是new一个HashMap了而是new了一个LinkedHashMap,这就是它能保证有序的关键,LinkedHashMap用双向连败OA的方式在HashMap的基础上额外保存了键值对的插入顺序
由于LinkedHashMap可以保证键值对的顺序所以用来实现简单的LRU缓存。
所以当你既要保证元素无重复,又要保证元素有序,可以使用LinkedHashSet