关于HashSet与HashMap的区别及说明

 更新时间:2023年07月26日 09:42:11   作者:huhahuha_  
这篇文章主要介绍了关于HashSet与HashMap的区别及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

HashSet与HashMap的区别

HashSet 集合不允许存储相同的元素, 它底层实际上使用 HashMap 来存储元素的,不过关注的只是key元素, 所有 value元素默认为 Object类对象.

HashSet源码如下

HashSet 的构造方法

//HashSet底层用来存储元素的结构,实际上使用HashMap来存储
private transient HashMap<E,Object> map;
//HashMap中的value值,HashSet只关注key值,所以所有的value值都为Object对象
private static final Object PRESENT = new Object();
//HashSet的无参构造,直接创建了一个HashMap对象
public HashSet() {
        map = new HashMap<>();
}
//指定初始化容量和负载因子
public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
}
//给定初始化容量
public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
}
public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
}

可以看到 HashSet的构造方法底层都是调用 HashMap的构造方法, 所以HashSet底层实际上是使用 HashMap 来作为存储结构.

当使用无参构造创建 HashSet对象时, 其实调用了 HashMap的无参构造创建了一个 HashMap对象, 所以 HashSet 的初始化容量也为16, 负载因子也为 0.75.

再来看看 HashSet 的 add() 方法的实现:

可以看到 HashSet 的 add() 方法底层实际也是调用了 HashMap 的 put() 方法, 这里的key为我们传入的将要添加到 set集合中的元素, 而value值则为 PERSENT,其实就是上面分析的 HashSet类中的一个静态字段, 默认为 Object对象.

HashSet并不关注value元素, 只使用 HashMap来存储 key元素, 这就使得 HashSet判断元素相等的条件与 HashMap中 key相等的条件其实是一样的, 两个元素的 hashCode值相同且通过equals()方法比较返回 true.

所以HashSet应该重写 equals()和hashCode()方法, 两个元素的 HashCode相同, 保证通过equals() 方法比较返回 true.

总结一下HashSet和HashMap的区别

(1)HashSet实现了Set接口, 仅存储对象; HashMap实现了 Map接口, 存储的是键值对.

(2)HashSet底层其实是用HashMap实现存储的, HashSet封装了一系列HashMap的方法. 依靠HashMap来存储元素值,(利用hashMap的key键进行存储), 而value值默认为Object对象. 所以HashSet也不允许出现重复值, 判断标准和HashMap判断标准相同, 两个元素的hashCode相等并且通过equals()方法返回true.

HashSet与HashMap的关系

HashSet作为一种最简单的java集合类,真的可以用三句话来概括一下:

第一句:存放不重复的数据。第二句:底层基于hash表实现。第三句:内部基于HashMap。

这也就是说,你想要完完全全彻彻底底地把HashSet吃透,就一定要先吃透HashMap。这篇文章将带着你从特点到存储,再到最后的实现,从源码角度来分析一下。

认识

HashSet其实就是一个没有重复数据的集合,基本用法很简单,我们直接给个例子。

以上只是列出了其最简单的用法。下面我们看看其继承关系。

HashSet主要继承了三个接口Serializable、Cloneable、Set,并且实现了抽象类AbstractSet。

我们直接看看源码:

学过HashMap的人应该都知道HashMap实现的是Map接口,而HashSet是Set接口。

下面我们就从源码的角度来分析一下HashSet。

源码分析

1、参数变量

这里有个问题,那就是既然HashSet只使用到了HashMap的key,为什么不使用null来充当HashMap的value,而使用了PRESENT这个对象呢?

答:想要深入这个问题,我们还需要深入到源码中看看:

以上两个是增删方法,在add一个元素的时候,其实调用的就是map.put(e, PRESENT)==null,HashMap在put元素的时候会出现两种情况:

情况一:put的元素是新的,那么map.put会发现key没有,那么直接插入即可。return结果为true。

情况二:put的元素是旧的,那么map.put会发现key已有,则直接返回相应的value,也就是PRESENT,PRESENT不等于null,return的也就是false了,表示HashSet插入失败。如果我们这里使用null为map.put的参数呢?直接返回相应的value,也就是null,这时候null==null是true。竟然返回了true。很明显就是错误的返回结果呀。

这其实也是去重复的原理。对于删除方法其实也是一样的。

2、构造函数

HashSet提供的构造方法很多,有5个,在这里我想说明的是每一种构造方法,其实都是创建的HashMap。这也证明了我们文章开头提到的内部基于HashMap。

3、其他方法

增删方法我们已经提到了,在这里我们主要看一下其他方法。

上面的方法还包含了遍历元素的方式。

HashSet就是这么简单,源码里面几乎所有的方法都是HashMap实现的。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java多线程中Lock的使用小结

    Java多线程中Lock的使用小结

    jdk1.5 以后,提供了各种锁,本文主要介绍了Java多线程中Lock的使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • SpringBoot使用Flyway进行数据库管理的操作方法

    SpringBoot使用Flyway进行数据库管理的操作方法

    Flyway是一个开源的数据库版本管理工具,并且极力主张“约定大于配置”,简单、专注、强大。接下来通过本文给大家介绍SpringBoot使用Flyway进行数据库管理的方法,感兴趣的朋友一起看看吧
    2021-09-09
  • SpringBoot工程Docker多环境中使用同一个Jar包解决方案

    SpringBoot工程Docker多环境中使用同一个Jar包解决方案

    在Docker多环境部署中,SpringBoot工程可以通过环境变量来动态改变配置,无需重新打包,利用volume挂载或docker cp命令,可以将配置文件直接传入容器,提高部署效率,并保证安全性
    2024-09-09
  • java基础之Object类

    java基础之Object类

    这篇文章主要介绍了java基础之Object类 的相关资料,需要的朋友可以参考下
    2015-06-06
  • Maven中的SnapShot版本和正式版本的区别

    Maven中的SnapShot版本和正式版本的区别

    在Nexus仓库中,一个仓库一般分为public(Release)仓和SNAPSHOT仓,本文详细的介绍了SnapShot版本和正式版本的区别,感兴趣的可以了解一下
    2021-06-06
  • 解决ObjectMapper序列换Map时候的坑

    解决ObjectMapper序列换Map时候的坑

    这篇文章主要介绍了解决ObjectMapper序列换Map时候的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • java8中NIO缓冲区(Buffer)的数据存储详解

    java8中NIO缓冲区(Buffer)的数据存储详解

    在本篇文章中小编给大家分享了关于java8中NIO缓冲区(Buffer)的数据存储的相关知识点,需要的朋友们参考下。
    2019-04-04
  • Java实现对华北、华南、华东和华中四个区域的划分

    Java实现对华北、华南、华东和华中四个区域的划分

    在Java中,通过定义枚举类、编写主程序和进行测试,本文详细介绍了如何划分华北、华南、华东和华中四个区域,首先定义枚举类标识区域,然后通过主程序接收用户输入并返回相应区域,最后通过测试用例确保正确性,文章还介绍了甘特图和饼状图的使用
    2024-09-09
  • 一文带你掌握springBoot如何做到优雅停机的

    一文带你掌握springBoot如何做到优雅停机的

    在分布式系统中,服务的优雅停机(Graceful Shutdown)是确保业务连续性的重要机制,下面就跟随小编一起来深入了解下springBoot实现优雅停机的具体方式吧
    2025-04-04
  • Struts和servlet不能共存问题解决方法

    Struts和servlet不能共存问题解决方法

    这篇文章主要介绍了Struts和servlet不能共存问题解决方法,共有三种方法,需要的朋友可以参考下。
    2017-09-09

最新评论