java集合框架Set子接口之HashSet源码剖析

感兴趣的话大家可以关注一下公众号 : 猿人刘先生 , 欢迎大家一起学习 , 一起进步 , 一起来交流吧!

HashSet

HashSet类实现了由哈希表(实际上是HashMap实例)支持的Set接口 , 底层采用HashMap来保存的数据 , 存在HashSet中的元素是无序且不重复的并且HashSet是线程不安全的 , 这种不重复其实是由HashMap实现的 , 所以HashSet的实现也是相对比较简单的 , 对于它的操作其实都是调用HashMap的方法来实现的

HashSet类结构图

java集合框架Set子接口之HashSet源码剖析_第1张图片

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中重复添加元素时并不会覆盖

你可能感兴趣的:(java,java,开发语言,数据结构)