Java集合之Set

485 阅读1分钟

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

Set 是 Java 中常用集合之一,其重点实现类包括 HashSet、LinkedHashSet 和 TreeSet。

Java集合-Set.png

HashSet

HashSet 通过 hash 表存储元素 ,其底层是 HashMap。HashSet 的数据就是 HashMap 的键,HashMap 的值为空对象。

  1. 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;
 }
  1. HashSet 的初始容量是多少,负载因子是多少?

因为 HashSet 的底层是 HashMap,所以其初始容量为 16,负载因子为 0.75。同样也可以根据其构造方法推测出该值。

 public HashSet(Collection<? extends E> c) {
     map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
     ...
 }
  1. 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<>()

Java集合-Set应用场景.png