摘要:注实际应用中,我们一般是用集合来存储相同的字符串的,不会用来存。解答虽然我们不能用来存放类型重复的字符串,但我们可以用来存储类型重复的字符串呀。而对于类型,相同字符串的不同对象哈希值是不同的。
有一种学得快的方法,就是不要一次学太多。1. 前言
今天,我们来探讨一个实际中不常用但却比较有意思的问题。它能帮助你理解 “HashSet中的键值是唯一的,不可重复的” 这句话的真正含义,也考验你对问题的思考深度。
注:实际应用中,我们一般是用 ArrayList 集合来存储相同的字符串的,不会用 HashSet 来存。
我们平时都看到或听说 HashSet 是不能用来存放重复的字符串的,是真的存放不了吗?如果面试问你这个问题,你能给出解决方案吗?
2. 参考解答先给出参考解答,然后我们再来分析为什么。
解答:
虽然我们不能用 HashSet 来存放 String 类型重复的字符串,但我们可以用 HashSet 来存储 StringBuilder 类型重复的字符串呀。
public class HashSetTest { public static void main(String[] args){ // 用 HashSet 来存放 String 类型的重复的字符串会发生什么? HashSeths1 = 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 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 说明两个对象不同。
具体验证思路如果你感兴趣,请查看: JDK 是如何判断两个对象是否相同的?判断的流程是什么?
那为什么相同字符串的不同 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 集合中了。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/73408.html
摘要:集合中成员很丰富,常用的集合有,,等。实现接口的集合主要有。集合中不能包含重复的元素,每个元素必须是唯一的。而以作为实现的构造函数的访问权限是默认访问权限,即包内访问权限。与接口不同,它是由一系列键值对组成的集合,提供了到的映射。 原文地址 Java集合 Java集合框架:是一种工具类,就像是一个容器可以存储任意数量的具有共同属性的对象。 Java集合中成员很丰富,常用的集合有Arra...
摘要:如果你知道用集合,就用。在集合中常见的数据结构底层数据结构是数组,查询快,增删慢底层数据结构是链表,查询慢,增删快底层数据结构是哈希表。依赖两个方法和底层数据结构是二叉树。 第三阶段 JAVA常见对象的学习 集合框架——Set接口 showImg(https://segmentfault.com/img/remote/1460000019683927?w=700&h=280); Lis...
摘要:并把最终的随机数输出到控制台。方法,在集合中如何存储元素取决于方法的返回值返回,集合中只有一个元素。创建集合对象,传入比较器。 1_HashSet存储字符串并遍历 A:Set集合概述及特点 通过API查看即可 B:案例演示 HashSet存储字符串并遍历 import java.util.HashSet; public class Demo1_HashSet { p...
摘要:接口的特点接口的特点它是一个元素存取有序的集合。导致迭代器并不知道集合中的变化,容易引发数据的不确定性。枚举已被迭代器替代。集合取出元素的方式可以采用迭代器增强。 01List接口的特点 A:List接口的特点: a:它是一个元素存取有序的集合。 例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。 b:它是一个带有索引的...
阅读 1291·2021-11-16 11:44
阅读 3757·2021-10-09 10:01
阅读 1744·2021-09-24 10:31
阅读 3831·2021-09-04 16:41
阅读 2510·2021-08-09 13:45
阅读 1209·2019-08-30 14:08
阅读 1774·2019-08-29 18:32
阅读 1639·2019-08-26 12:12