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

简介: 无序之美: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


 

相关文章
|
2天前
|
Java 测试技术
Java一分钟之-正则表达式在Java中的应用
【5月更文挑战第14天】正则表达式是Java中用于文本处理的强大力量,通过`java.util.regex`包支持。常见问题包括元字符的理解、边界匹配和贪婪/懒惰量词的使用。错误通常涉及未转义特殊字符、不完整模式或过度匹配。要避免这些问题,需学习实践、使用在线工具和测试调试。示例代码展示了如何验证邮箱地址。掌握正则表达式需要不断练习和调试。
17 2
|
23小时前
|
安全 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【5月更文挑战第16天】 在移动开发领域,性能一直是开发者关注的焦点。随着Kotlin语言的普及,其与Java在Android应用中的性能表现成为热门话题。本文将深入分析Kotlin和Java在Android平台上的性能差异,并通过实际测试数据来揭示二者在编译速度、应用启动时间以及运行效率方面的表现。我们的目标是为开发者提供一个参考依据,以便在选择合适的编程语言时做出更加明智的决策。
|
2天前
|
存储 安全 算法
【常见集合】Java 常见集合重点解析
【常见集合】Java 常见集合重点解析
6 0
|
2天前
|
前端开发 Java 测试技术
Java一分钟之Spring MVC:构建Web应用
【5月更文挑战第15天】Spring MVC是Spring框架的Web应用模块,基于MVC模式实现业务、数据和UI解耦。常见问题包括:配置DispatcherServlet、Controller映射错误、视图解析未设置、Model数据传递遗漏、异常处理未配置、依赖注入缺失和忽视单元测试。解决这些问题可提升代码质量和应用性能。注意配置`web.xml`、`@RequestMapping`、`ViewResolver`、`Model`、`@ExceptionHandler`、`@Autowired`,并编写测试用例。
51 3
|
2天前
|
机器学习/深度学习 数据采集 自然语言处理
理解并应用机器学习算法:神经网络深度解析
【5月更文挑战第15天】本文深入解析了神经网络的基本原理和关键组成,包括神经元、层、权重、偏置及损失函数。介绍了神经网络在图像识别、NLP等领域的应用,并涵盖了从数据预处理、选择网络结构到训练与评估的实践流程。理解并掌握这些知识,有助于更好地运用神经网络解决实际问题。随着技术发展,神经网络未来潜力无限。
|
2天前
|
Java 开发工具 Maven
java解析apk获取应用信息
请注意,你需要替换"path/to/your/apkfile.apk"为你的APK文件的实际路径。
10 0
|
2天前
|
Java 编译器 开发者
Java一分钟之-Java注解的理解与应用
【5月更文挑战第12天】本文介绍了Java注解的基础知识和常见应用,包括定义、应用和解析注解。注解在编译检查、框架集成和代码生成等方面发挥重要作用。文章讨论了两个易错点:混淆保留策略和注解参数类型限制,并提供了避免策略。提醒开发者避免过度使用注解,以保持代码清晰。理解并恰当使用注解能提升代码质量。
13 3
|
2天前
|
Java API 开发者
Java中Lambda表达式的深入理解与应用
【5月更文挑战第12天】在Java 8之后,Lambda表达式已经成为了Java开发者必备的技能之一。Lambda表达式以其简洁、灵活的特点,大大提高了编程的效率。本文将深入探讨Lambda表达式的基本概念,语法规则,以及在实际开发中的应用,帮助读者更好地理解和使用Lambda表达式。
|
Java
Java HashSet LinkedHashSet TreeSet类源码解析
Set集合中不含有重复的元素,插入重复的元素会失败。常用的有HashSet LinkedHashSet TreeSet。HashSet是无序的集合,LinkedHashSet中的排序和插入成功的顺序一致重复插入,TreeSet中元素是有序排列的,排序的依据是自身的comparator如果为null则根据key从小到大排序。
2152 0
|
Java 算法
HashSet源码解析(基于Java8)
List保证元素的添加顺序,元素可重复 Set不保证元素的添加顺序,元素不可重复 public class Test { public static void main(String[] arg...
1085 0

推荐镜像

更多