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

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

HashSet

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


HashSet类结构图

39e63c8e94ea7089887d527f04c4c0e6_9b0ca548465b43150ee3106752399bdc.png


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

相关文章
|
10天前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
32 5
|
22天前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
34 4
|
1月前
set集合
HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素。 LinkedHashSet: LinkedHashSet 是 HashSet 的子类,并且其内部是通过 LinkedHashMap 来实现的。 TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树)。
|
1月前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
31 2
|
1月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
17天前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
52 18
你对Collection中Set、List、Map理解?
|
11天前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
48 20
|
27天前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
30 3
【C++】map、set基本用法
|
27天前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
21 5
|
2月前
|
存储 JavaScript 前端开发
Set、Map、WeakSet 和 WeakMap 的区别
在 JavaScript 中,Set 和 Map 用于存储唯一值和键值对,支持多种操作方法,如添加、删除和检查元素。WeakSet 和 WeakMap 则存储弱引用的对象,有助于防止内存泄漏,适合特定场景使用。