这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战
Set 是 Java 中常用集合之一,其重点实现类包括 HashSet、LinkedHashSet 和 TreeSet。
HashSet
HashSet 通过 hash 表存储元素 ,其底层是 HashMap。HashSet 的数据就是 HashMap 的键,HashMap 的值为空对象。
- HashSet 的底层是什么?
由其构造方法可以发现,当我们创建一个 HashSet 对象时,其实就是创建了一个键为指定数据,值为空 Object 类型的 HashMap。
// 用来存储 HashSet 数据的变量
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
// PRESENT 为 Object 类型的空对象
return map.put(e, PRESENT)==null;
}
- HashSet 的初始容量是多少,负载因子是多少?
因为 HashSet 的底层是 HashMap,所以其初始容量为 16,负载因子为 0.75。同样也可以根据其构造方法推测出该值。
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
...
}
- HashSet 能保证保证元素的顺序吗?
HashSet 通过 hash 表存储元素,因此一般认为 HashSet 插入的元素是无序的。但是也可以通过指定构造方法(该方法的权限为 default)来保持插入数据的顺序,此时其底层为 LinkedHashMap。而这也就是为什么 LinkedHashSet 能维持元素有序的原因。
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
LinkedHashSet
LinkedHashSet 的底层为 LinkedHashMap,其链表保证了元素的插入与遍历顺序,哈希表保证了元素的唯一性。LinkedHashSet 的初始容量同样为 16,负载因子为 0.75。
public LinkedHashSet() {
super(16, .75f, true);
}
TreeSet
TreeSet 通过二叉树存储数据,其底层是 TreeMap。二叉树结构保证了元素的顺序,默认为自然排序,可以通过实现 Compareable 接口,并重写里面的compareTo() 方法来进行自定义排序。
public TreeSet() {
this(new TreeMap<E,Object>());
}
HashSet 与 LinkedHashSet 的区别
HashSet 不能保证元素的顺序,而 LinkedHashSet 可以保证元素的顺序。
TreeSet 与 LinkedHashSet 的区别
LinkedHashSet 保证的是元素的插入与遍历顺序一致,而 TreeSet 是插入时根据规则排好序,其有序性的意义是不同的。
总结
HashSet、LinkedHashSet 和 TreeSet 都是线程不安全的,在多线程环境中,可以使用Collections.synchronizedSet(new HashSet<>())
或者new CopyOnWriteArraySet<>()
。