我的编程空间,编程开发者的网络收藏夹
学习永远不晚

HashSet怎么保证元素不重复

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

HashSet怎么保证元素不重复

小编给大家分享一下HashSet怎么保证元素不重复,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

HashSet 实现了 Set 接口,由哈希表(实际是 HashMap)提供支持。HashSet 不保证集合的迭代顺序,但允许插入 null 值。也就是说 HashSet 不能保证元素插入顺序和迭代顺序相同。
HashSet 具备去重的特性,也就是说它可以将集合中的重复元素自动过滤掉,保证存储在 HashSet 中的元素都是唯一的。

1.HashSet 基本用法

HashSet 基本操作方法有:add(添加)、remove(删除)、contains(判断某个元素是否存在)和 size(集合数量)。这些方法的性能都是固定操作时间,如果哈希函数是将元素分散在桶中的正确位置。
HashSet 基本使用如下:

// 创建 HashSet 集合HashSet<String> strSet = new HashSet<>();// 给 HashSet 添加数据strSet.add("Java");strSet.add("MySQL");strSet.add("Redis");// 循环打印 HashSet 中的所有元素strSet.forEach(s -> System.out.println(s));

2.HashSet 无序性

HashSet 不能保证插入元素的顺序和循环输出元素的顺序一定相同,也就是说 HashSet 其实是无序的集合,具体代码示例如下:

HashSet<String> mapSet = new HashSet<>();mapSet.add("深圳");mapSet.add("北京");mapSet.add("西安");// 循环打印 HashSet 中的所有元素mapSet.forEach(m -> System.out.println(m));

以上程序的执行结果如下:

HashSet怎么保证元素不重复

从上述代码和执行结果可以看出,HashSet 插入的顺序是:深圳 -> 北京 -> 西安,而循环打印的顺序却是:西安 -> 深圳 -> 北京,所以 HashSet 是无序的,不能保证插入和迭代的顺序一致。

PS:如果要保证插入顺序和迭代顺序一致,可使用 LinkedHashSet 来替换 HashSet。

3.HashSet 错误用法

有人说 HashSet 只能保证基础数据类型不重复,却不能保证自定义对象不重复?这样说对吗?
我们通过以下示例来说明此问题。

3.1 HashSet 与基本数据类型

使用 HashSet 存储基本数据类型,实现代码如下:

HashSet<Long> longSet = new HashSet<>();longSet.add(666l);longSet.add(777l);longSet.add(999l);longSet.add(666l);// 循环打印 HashSet 中的所有元素longSet.forEach(l -> System.out.println(l));

以上程序的执行结果如下:

HashSet怎么保证元素不重复

从上述结果可以看出,使用 HashSet 可以保证基础数据类型不重复。

3.2 HashSet 与自定义对象类型

接下来,将自定义对象存储到 HashSet 中,实现代码如下:

public class HashSetExample {    public static void main(String[] args) {        HashSet<Person> personSet = new HashSet<>();        personSet.add(new Person("曹操", "123"));        personSet.add(new Person("孙权", "123"));        personSet.add(new Person("曹操", "123"));        // 循环打印 HashSet 中的所有元素        personSet.forEach(p -> System.out.println(p));    }}@Getter@Setter@ToStringclass Person {    private String name;    private String password;    public Person(String name, String password) {        this.name = name;        this.password = password;    }}

以上程序的执行结果如下:

HashSet怎么保证元素不重复

从上述结果可以看出,自定义对象类型确实没有被去重,那也就是说 HashSet 不能实现自定义对象类型的去重咯?
其实并不是,HashSet 去重功能是依赖元素的 hashCode 和 equals 方法判断的,通过这两个方法返回的都是 true 那就是相同对象,否则就是不同对象。而前面的 Long 类型元素之所以能实现去重,正是因为 Long 类型中已经重写了 hashCode 和 equals 方法,具体实现源码如下:

@Overridepublic int hashCode() {    return Long.hashCode(value);}public boolean equals(Object obj) {    if (obj instanceof Long) {        return value == ((Long)obj).longValue();    }    return false;}//省略其他源码......

更多关于 hashCode 和 equals 的内容,详见:https://www.yisu.com/article/204554.htm

那么,想让 HashSet 支持自定义对象去重,只需要在自定义对象中重写 hashCode 和 equals 方法即可,具体实现代码如下:

@Setter@Getter@ToStringclass Person {    private String name;    private String password;    public Person(String name, String password) {        this.name = name;        this.password = password;    }    @Override    public boolean equals(Object o) {        if (this == o) return true; // 引用相等返回 true        // 如果等于 null,或者对象类型不同返回 false        if (o == null || getClass() != o.getClass()) return false;        // 强转为自定义 Person 类型        Person persion = (Person) o;        // 如果 name 和 password 都相等,就返回 true        return Objects.equals(name, persion.name) &&                Objects.equals(password, persion.password);    }    @Override    public int hashCode() {        // 对比 name 和 password 是否相等        return Objects.hash(name, password);    }}

重新运行以上代码,执行结果如下图所示:

HashSet怎么保证元素不重复

从上述结果可以看出,之前的重复项“曹操”已经被去重了。

4.HashSet 如何保证元素不重复?

我们只要了解了 HashSet 执行添加元素的流程,就能知道为什么 HashSet 能保证元素不重复了?
HashSet 添加元素的执行流程是:当把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现,会将对象插入到相应的位置中。但是如果发现有相同 hashcode 值的对象,这时会调用对象的 equals() 方法来检查对象是否真的相同,如果相同,则 HashSet 就不会让重复的对象加入到 HashSet 中,这样就保证了元素的不重复。

为了更清楚的了解 HashSet 的添加流程,我们可以尝试阅读 HashSet 的具体实现源码,HashSet 添加方法的实现源码如下(以下源码基于 JDK 8):

// hashmap 中 put() 返回 null 时,表示操作成功public boolean add(E e) {    return map.put(e, PRESENT)==null;}

从上述源码可以看出 HashSet 中的 add 方法,实际调用的是 HashMap 中的 put,那么我们继续看 HashMap 中的 put 实现:

// 返回值:如果插入位置没有元素则返回 null,否则返回上一个元素public V put(K key, V value) {    return putVal(hash(key), key, value, false, true);}

从上述源码可以看出,HashMap 中的 put() 方法又调用了 putVal() 方法,putVal() 的源码如下:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,                   boolean evict) {        Node<K, V>[] tab;        Node<K, V> p;        int n, i;        //如果哈希表为空,调用 resize() 创建一个哈希表,并用变量 n 记录哈希表长度        if ((tab = table) == null || (n = tab.length) == 0)            n = (tab = resize()).length;                if ((p = tab[i = (n - 1) & hash]) == null)            // 直接将键值对插入到 map 中即可            tab[i] = newNode(hash, key, value, null);        else {// 桶中已经存在元素            Node<K, V> e;            K k;            // 比较桶中第一个元素(数组中的结点)的 hash 值相等,key 相等            if (p.hash == hash &&                    ((k = p.key) == key || (key != null && key.equals(k))))                // 将第一个元素赋值给 e,用 e 来记录                e = p;                // 当前桶中无该键值对,且桶是红黑树结构,按照红黑树结构插入            else if (p instanceof TreeNode)                e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);                // 当前桶中无该键值对,且桶是链表结构,按照链表结构插入到尾部            else {                for (int binCount = 0; ; ++binCount) {                    // 遍历到链表尾部                    if ((e = p.next) == null) {                        p.next = newNode(hash, key, value, null);                        // 检查链表长度是否达到阈值,达到将该槽位节点组织形式转为红黑树                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st                            treeifyBin(tab, hash);                        break;                    }                    // 链表节点的<key, value>与 put 操作<key, value>                    // 相同时,不做重复操作,跳出循环                    if (e.hash == hash &&                            ((k = e.key) == key || (key != null && key.equals(k))))                        break;                    p = e;                }            }            // 找到或新建一个 key 和 hashCode 与插入元素相等的键值对,进行 put 操作            if (e != null) { // existing mapping for key                // 记录 e 的 value                V oldValue = e.value;                                if (!onlyIfAbsent || oldValue == null)                    e.value = value;                // 访问后回调                afterNodeAccess(e);                // 返回旧值                return oldValue;            }        }        // 更新结构化修改信息        ++modCount;        // 键值对数目超过阈值时,进行 rehash        if (++size > threshold)            resize();        // 插入后回调        afterNodeInsertion(evict);        return null;    }

从上述源码可以看出,当将一个键值对放入 HashMap 时,首先根据 key 的 hashCode() 返回值决定该 Entry 的存储位置。如果有两个 key 的 hash 值相同,则会判断这两个元素 key 的 equals() 是否相同,如果相同就返回 true,说明是重复键值对,那么 HashSet 中 add() 方法的返回值会是 false,表示 HashSet 添加元素失败。因此,如果向 HashSet 中添加一个已经存在的元素,新添加的集合元素不会覆盖已有元素,从而保证了元素的不重复。如果不是重复元素,put 方法最终会返回 null,传递到 HashSet 的 add 方法就是添加成功。

以上是“HashSet怎么保证元素不重复”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

HashSet怎么保证元素不重复

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

HashSet怎么保证元素不重复

小编给大家分享一下HashSet怎么保证元素不重复,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!HashSet 实现了 Set 接口,由哈希表(实际是 HashM
2023-06-22

php怎么去掉重复元素

本篇内容介绍了“php怎么去掉重复元素”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!php去掉重复元素的方法是,使用array_unique
2023-06-20

python列表怎么找出重复元素

可以使用python中的集合(set)来找出列表中的重复元素。集合是一种无序且不重复的数据结构,所以将列表转换为集合后,再将集合转换回列表,就可以删除重复元素。以下是一个示例代码:```pythondef find_duplicates(l
2023-09-29

python怎么删除列表重复元素

要删除列表中的重复元素,可以使用set()函数和list()函数的组合来实现。例如,假设有一个列表nums,其中包含了重复的元素:nums = [1, 2, 3, 1, 2, 3, 4, 5]可以使用set()函数来去除重复元素,然后再将其
2023-10-24

JavaScript怎么找出数组中的重复或非重复元素

本文小编为大家详细介绍“JavaScript怎么找出数组中的重复或非重复元素”,内容详细,步骤清晰,细节处理妥当,希望这篇“JavaScript怎么找出数组中的重复或非重复元素”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习
2023-06-29

Java中怎么删除ArrayList的重复元素

Java中怎么删除ArrayList的重复元素,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。方法1:使用HashSet删除ArrayList中重复的元素在该方法
2023-06-17

python怎么删除数组中重复的元素

可以通过使用set()函数或者使用列表推导式来删除数组中的重复元素。方法一:使用set()函数```pythona = [1, 2, 3, 3, 4, 5, 5]b = list(set(a))print(b)```输出:```[1, 2,
2023-08-19

java怎么判断数组元素是否重复

Java中判断数组元素是否重复可以使用以下方法:1. 使用两层循环遍历数组,比较每对元素是否相同。如果找到相同的元素,则数组中存在重复元素。```javapublic static boolean isDuplicate(int[] arr
2023-09-22

php数组怎么去除重复和空元素

实现步骤:1、使用array_unique()函数去除原数组中的重复元素,语法“array_unique(原数组)”,会返回一个去重数组;2、使用array_filter()函数过滤去重数组,删除去重数组中的空元素即可,语法“array_filter(去重数组)”。本教程操作环境:windows7系统、PHP8.1版、DELL G3电脑在php中,可以array_unique()函数和array_f
2022-06-28

python怎么删除矩阵中重复的元素

要删除矩阵中重复的元素,可以使用嵌套循环和条件判断来实现。以下是一个示例代码:```pythonmatrix = [[1, 2, 3], [4, 5, 2], [6, 7, 8], [1, 2, 3], [4, 5, 2]]# 创建一个空列
2023-08-19

使用Python怎么删除列表重复元素

本篇文章为大家展示了使用Python怎么删除列表重复元素,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。python有哪些常用库python常用的库:1.requesuts;2.scrapy;3.pi
2023-06-14

python怎么去除列表中的重复元素

可以使用set()函数将列表转换为集合,再将集合转换为列表,即可去除列表中的重复元素。具体代码如下:```python# 原始列表lst = [1, 2, 3, 4, 2, 3, 1, 5]# 去除重复元素后的列表lst_unique =
2023-09-23

PHP数组中的重复元素怎么删除

这篇文章主要介绍“PHP数组中的重复元素怎么删除”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“PHP数组中的重复元素怎么删除”文章能帮助大家解决问题。一、使用array_unique()函数去重PH
2023-07-06

怎么实现PHP删除数组重复元素

这篇文章主要为大家展示了“怎么实现PHP删除数组重复元素”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“怎么实现PHP删除数组重复元素”这篇文章吧。我们在实际操作PHP代码的过程中,经常会遇到数组
2023-06-17

python怎么判断列表有没有重复元素

可以使用set()函数来判断列表中是否有重复元素。set()函数会将列表转换为集合,并且集合中的元素是唯一的,没有重复的元素。下面是一个判断列表中是否有重复元素的例子:def has_duplicates(lst):# 将列表转换为集合
2023-10-27

c语言怎么删除数组中重复元素

删除数组中重复元素的方法可以分为两种:使用额外的空间:创建一个新数组,遍历原数组,将不重复的元素存入新数组。将新数组复制回原数组。int removeDuplicates(int arr[], int n) {if (n == 0 ||
c语言怎么删除数组中重复元素
2024-03-01

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录