无序之美:Java HashSet解析与应用

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 无序之美:Java HashSet解析与应用

HashSet 底层基于 HashMap 来实现,是一个不允许有重复元素并且无序的集合。HashSet 允许有 null 值,但是只能有一个。HashSet 不是线程安全的,如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。HashSet 继承自 AbstractSet,并且实现了 Set、Cloneable、Serializable 接口。

HashSet 还有一个子类,LinkedHashSet,LinkedHashSet 集合也是根据元素的 hashCode 值来决定元素的存储位置,但是它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序来保存的。也就是说,当遍历 LinkedHashSet 集合里的元素时,会按照元素插入顺序来访问元素。

总的来说,HashSet 只是在 HashMap 的基础上包装了一层,基本都使用的 HashMap 的方法。

成员变量

容器 map

private transient HashMap<E,Object> map;

使用 HashMap 的 key 保存 HashSet 中所有元素。

静态 map 值

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

定义一个静态的 Object 对象作为上面定义的 HashMap 的 value。

由于 HashSet 底层使用的是 map 中的键来存储数据,那么在新增数据的时候,原来 map 的值全部使用这个静态的对象。即调用map.put(数据, PRESENT)

构造方法

无参构造:默认创建 HashMap

public HashSet() {
    map = new HashMap<>();
}

无参数的构造函数,此构造函数创建一个大小为 16,加载因子为 0.75 的 HashMap 用于存储数据(联系到前面学习的HashMap)。

指定初始化集合

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

为了避免扩容操作,首先需要对初始化集合的大小与默认大小 16 进行比较,取最大然后初始化 map。之后调用 addAll 方法将初始化集合中的元素添加到 HashSet 中的 map 的 key 中。

指定容量和负载因子

public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

指定容量

public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

这里直接调用的是 HashMap 的构造方法(联系到前面学习的HashMap)。

由 HashSet 的成员变量和构造方法可以看出,HashSet 本质还是在内部维护一个 HashMap 对象,将所有的数据都交给 HashMap 进行处理。

成员方法

新增元素 add

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

将指定元素存入 HashSet,内部实现就是将指定元素作为 key,用常量对象 PRESENT 作为 value 存入 HashMap。

删除元素 remove

public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

从集合中删除指定的元素,如果集合包含指定的元素,则返回 true。同样调用的 HashMap 的 remove 方法。

是否包含元素 contains

public boolean contains(Object o) {
    return map.containsKey(o);
}

contains 方法的目的是检查 HashSet 中是否存在元素。如果找到该元素,则返回 true,否则返回 false。本质上调用的 HashMap 的 containsKey 方法。

集合大小 size

public int size() {
    return map.size();
}

调用 HashMap 的 size 方法,返回当前集合的大小。

HashSet 是否是空的 isEmpty

public boolean isEmpty() {
    return map.isEmpty();
}

调用 HashMap 的 isEmpty 方法,判断当前集合是否是空的。

清空元素 clear

public void clear() {
    map.clear();
}

调用 HashMap 的 clear 方法,将当前集合清空。原理是遍历 HashMap 中的桶数组,将桶中的每个位置置为 null(联系到前面学习的HashMap)。

遍历 HashSet

获取迭代器 iterator

public Iterator<E> iterator() {
    return map.keySet().iterator();
}

本质上获取到的是内部维护的 map 的 keySet(键集)的迭代器对象。

LinkedHashSet

LinkedHashSet 是 HashSet 的子类,通过源码发现它是一个空壳,构造方法都调用 HashSet 的一个 default (没有使用访问修饰符修饰)构造方法 通过 HashSet 的这个 default 构造方法可知,LinkedHashSet 也是在 LinkedHashMap 基础之上包装了一层。

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

线程安全的使用

使用 Colletcions 这个工具类的 synchronizedSet 方法类创建个线程安全的 Set 集合。

public class HelloWorld {
    public static void main(String[] args) {
        HashSet<Object> hashSet = new HashSet<>();
        Set<Object> synchronizedSet = Collections.synchronizedSet(hashSet);
    }
}

笔记大部分摘录自《Java核心技术卷I》,含有少数本人修改补充痕迹。

参考文章:http://985.so/mmhca


 

相关文章
|
5天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
16 2
|
8天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
57 6
|
8天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
RS-485网络中的标准端接与交流电端接应用解析
RS-485,作为一种广泛应用的差分信号传输标准,因其传输距离远、抗干扰能力强、支持多点通讯等优点,在工业自动化、智能建筑、交通运输等领域得到了广泛应用。在构建RS-485网络时,端接技术扮演着至关重要的角色,它直接影响到网络的信号完整性、稳定性和通信质量。
|
6天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
18 4
|
9天前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####
|
5天前
|
存储 供应链 物联网
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
|
5天前
|
存储 供应链 安全
深度解析区块链技术的核心原理与应用前景
深度解析区块链技术的核心原理与应用前景
12 0
|
9天前
|
SQL 监控 安全
员工上网行为监控软件:SQL 在数据查询监控中的应用解析
在数字化办公环境中,员工上网行为监控软件对企业网络安全和管理至关重要。通过 SQL 查询和分析数据库中的数据,企业可以精准了解员工的上网行为,包括基础查询、复杂条件查询、数据统计与分析等,从而提高网络管理和安全防护的效率。
22 0
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
67 0

推荐镜像

更多