HashSet
HashSet类实现了由哈希表(实际上是HashMap实例)支持的Set接口 , 底层采用HashMap来保存的数据 , 存在HashSet中的元素是无序且不重复的并且HashSet是线程不安全的 , 这种不重复其实是由HashMap实现的 , 所以HashSet的实现也是相对比较简单的 , 对于它的操作其实都是调用HashMap的方法来实现的
HashSet类结构图
HashSet基本属性
// 保存数据的容器 private transient HashMap<E,Object> map; // Dummy value to associate with an Object in the backing Map // 存储在HashMap中的key对应的value值 private static final Object PRESENT = new Object();
HashSet构造方法
loadFactor: 负载因子 , 用来计算HashMap扩容时的阈值
initialCapacity : HashMap的容量
HashMap的阈值计算公式 : 阈值(threshold) = 扩容因子(loadFactor) * 容量(capacity)
如果扩容因子和容量都是默认的话 , 那么阈值就是 0.75f * 16 = 12
如果当HaspMap中table(也称为桶)的长度大于等于阈值时就会触发扩容机制
// HashSet默认无参构造方法 , 直接初始化一个Map public HashSet() { map = new HashMap<>(); } // 使用默认负载因子计算出HashMap的初始容量 , 并且和16取最大值 , 然后将传入的集合添加到HashSet的构造器 public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } // 使用指定的容量和负载因子进行HashMap的初始化 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } // 使用指定的容量进行HashMap的初始化 public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } // 初始化一个只能由 LinkedHashSet使用的HashSet集合 , 并且指定负载因子和初始容量 HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); } // 将一个集合的元素添加到HashMap中 public boolean addAll(Collection<? extends E> c) { boolean modified = false; for (E e : c) if (add(e)) modified = true; return modified; }
HashSet常用方法
// HashSet的长度 public int size() { return map.size(); } // 如果此集合不包含任何元素,则返回true public boolean isEmpty() { return map.isEmpty(); } // 如果此集合包含指定元素 , 则返回true public boolean contains(Object o) { return map.containsKey(o); } // 添加一个元素 , 如果元素不存在HashMap则调用HashMap的put()方法添加并返回true , 否则的话不进行任何操作并且直接返回false public boolean add(E e) { return map.put(e, PRESENT)==null; } // 移除一个元素 , 如果元素不存在HashMap则调用HashMap的remove()方法移出并返回true , 否则的话不进行任何操作并且直接返回false public boolean remove(Object o) { return map.remove(o)==PRESENT; } // 从此集合中移除所有元素。此调用返回后,该集合将为空。 public void clear() { map.clear(); }
总结
通过源码我们可以看出 , HashSet的底层通过HashMap来实现的 , 而HashMap在1.7之前使用的是数组+链表 , 在1.8之后使用的是数据+链表+红黑树实现 , 因为HashMap的无序 , 不可重复及线程不安全 , 所以HashSet也是如此 , 通常往HashSet中重复添加元素时并不会覆盖