一个有趣的问题: 如何用HashSet来存储重复的字符串?

栏目: Java · 发布时间: 5年前

内容简介:今天,我们来探讨一个实际中不常用但却比较有意思的问题。它能帮助你理解 “HashSet中的键值是唯一的,不可重复的” 这句话的真正含义,也考验你对问题的思考深度。注:实际应用中,我们一般是用 ArrayList 集合来存储相同的字符串的,不会用 HashSet 来存。我们平时都看到或听说 HashSet 是不能用来存放重复的字符串的,是真的存放不了吗?如果面试问你这个问题,你能给出解决方案吗?

1. 前言

今天,我们来探讨一个实际中不常用但却比较有意思的问题。它能帮助你理解 “HashSet中的键值是唯一的,不可重复的” 这句话的真正含义,也考验你对问题的思考深度。

注:实际应用中,我们一般是用 ArrayList 集合来存储相同的字符串的,不会用 HashSet 来存。

我们平时都看到或听说 HashSet 是不能用来存放重复的字符串的,是真的存放不了吗?如果面试问你这个问题,你能给出解决方案吗?

2. 参考解答

先给出参考解答,然后我们再来分析为什么。

解答:

虽然我们不能用 HashSet 来存放 String 类型重复的字符串,但我们可以用 HashSet 来存储 StringBuilder 类型重复的字符串呀。

public class HashSetTest {
    public static void main(String[] args){

        // 用 HashSet 来存放 String 类型的重复的字符串会发生什么?

        HashSet<String> hs1 = new HashSet<>();
        String s1 = new String("aaa");
        String s2 = new String("aaa");
        String s3 = new String("aaa");
        hs1.add(s1);
        hs1.add(s2);
        hs1.add(s3);
        System.out.println("hs1:"+hs1); // 重复的字符串是存不进去的

        // 用 HashSet 来存放 StringBuilder 类型的重复的字符串又会发生什么?

        HashSet<StringBuilder> hs2 = new HashSet<>();
        StringBuilder sb1 = new StringBuilder("aaa");
        StringBuilder sb2 = new StringBuilder("aaa");
        StringBuilder sb3 = new StringBuilder("aaa");
        hs2.add(sb1);
        hs2.add(sb2);
        hs2.add(sb3);
        System.out.println("hs2:"+hs2); // 咦,结果发现重复的字符串也能存进去了


        // 那为什么呢?我们来打印一个各个对象的hashCode看一下

        System.out.println("s1的hashCode:"+s1.hashCode());
        System.out.println("s2的hashCode:"+s2.hashCode());
        System.out.println("s3的hashCode:"+s3.hashCode());
        System.out.println("sb1的hashCode:"+sb1.hashCode());
        System.out.println("sb2的hashCode:"+sb2.hashCode());
        System.out.println("sb3的hashCode:"+sb3.hashCode());

    }
}

输出结果:

hs1:[aaa]
hs2:[aaa, aaa, aaa]
s1的hashCode:96321
s2的hashCode:96321
s3的hashCode:96321
sb1的hashCode:356573597
sb2的hashCode:1735600054
sb3的hashCode:21685669

从打印结果来看,我们是不能用 HashSet 来存放 String 类型的重复字符串的(如hs1),但我们是可以用HashSet来存放 StringBuilder 类型的重复字符串。

3. 为什么?

从打印的 hashCode 来看,String 类型,相同字符串的不同 String 对象哈希值是一样的。而对于 StringBuilder 类型,相同字符串的不同对象哈希值是不同的。

要知道这个问题的答案,我们首先得了解 Java 虚拟机是如何判断两个对象是否相同的。

那 Java 虚拟机是如何判断两个对象是否相同的呢?

参考解答:

Java 虚拟机会先判断两个对象的 hashCode 是否相同,如果 hashCode 不同,则说明肯定是两个不同的对象了;如果 hashCode 相同再通过 equals() 方法进行进一步比较,如果 equals 方法返回 true,则说明两个对象是相同的,如果equals方法返回 false 说明两个对象不同。

具体验证思路如果你感兴趣,请查看: 你有没有想过: Java 虚拟机是如何判断两个对象是否相同的?判断的流程是什么?

那为什么相同字符串的不同 String 对象哈希值是一样的,而且还被虚拟机判断为相同的对象了呢?

因为 String 类复写了 Object 类的 hashCode() 和 equals() 方法,并实现了自己的 hashCode 值生成算法和 equals 的比较规则,具有相同字符串内容的不同 String 对象在初始化时生成的 hashCode 值是一样的,并且 String 类 equals() 方法比较的是两个字符串的内容,而不是内存地址值,这两个条件同时成立, 这就使 Java 虚拟机把具有相同内容的不同 String 对象判断为相同的对象了,就不会存入 HashSet 集合中。

而 StringBuilder 为什么就可以呢?它相同内容的不同对象的哈希值值为什么是不同的?

查看 StringBuilder 类的源码你会发现,因为 StringBuilder 并没有复写 Object 类的 hashCode() 方法和 equals() 方法,StringBuilder 用的是父类 Object 类的 hashCode 生成算法,也就是用 native 层的 hashCode 生成算法,很大概率产生的哈希值是不一样的,即使产生了一样的哈希值,Object 类的 equals() 方法比较的是两个对象的内存地址,而不是两个对象的内容,这就使 Java 虚拟机把具有相同内容的 StringBuilder 对象判断为不同的对象,就可以存入 HashSet 集合中了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

文本上的算法——深入浅出自然语言处理

文本上的算法——深入浅出自然语言处理

路彦雄 / 人民邮电出版社 / 2018-3-1 / 69.00元

本书结合作者多年学习和从事自然语言处理相关工作的经验,力图用生动形象的方式深入浅出地介绍自然语言处理的理论、方法和技术。本书抛弃掉繁琐的证明,提取出算法的核心,帮助读者尽快地掌握自然语言处理所必备的知识和技能。本书主要分两大部分。第一部分是理论篇,包含前3章内容,主要介绍一些基础的数学知识、优化理论知识和一些机器学习的相关知识。第二部分是应用篇,包含第4章到第8章,分别针对计算性能、文本处理的术语......一起来看看 《文本上的算法——深入浅出自然语言处理》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具