Java 集合 HashSet 的 add 方法的分析

HashSet 散列码与 hashCode 方法解析

1). 简述

java.lang.Object: int hashCode() ;返回对象的散列码,散列码可以是任意的整数,包括正数或负数;可被重写。

值得注意的是两个对象相等,那么它们两个应该有相同的散列码(即当我们重写实现 hashCode 方法时,我们应当注意与 equals 方法的兼容);

例如:如果 x.equals(y) == true 或 y.equals(x) == true ,那么 x.hashCode() == y.hashCode()

2). hashCode 方法的底层实现原理

a. 每个对象都有一个默认的散列码,即 Object.hashCode():返回的散列码是当前对象的存储地址(以 int 类型返回) ,我们亦可以自己重写 hashCode 方法,自己规定散列码的计算方式。

b. String 类的 hashCode 方法:String 类就重写了 Object 的 hashCode 方法,重新规定了计算其对象的散列码的方式

int hash = 0;
for(int i = 0; i < length; i++)
	hash = 31*hash + charAt(i); // str.charAt(indx) 返回的是字符串 str 的第 index 个位置的字符
3). 与 HashSet 的 add 方法相关的一个问题

Set 接口的实现类无法向集合内添加重复的元素,因此判断下方的元素能否添加成功( Node 是自定义类,没有重写 hashCode 方法)

HashSet<String> strings = new HashSet<>();

System.out.println(strings.add("Winters")); // true
System.out.println(strings.add("Winters")); // false

System.out.println(strings.add(new String("Easy Company"))); // true
System.out.println(strings.add(new String("Easy Company"))); // false

HashSet<Node> nodes = new HashSet<>();

System.out.println(nodes.add(new Node("Winters", null))); // true
System.out.println(nodes.add(new Node("Winters", null))); // true

HashSet 的 add 方法的原理是先计算所添加的对象元素的散列码,再根据散列码来决定并寻找所添加的对象元素在集合中的位置,一旦发现目标位置已经存在元素了(散列冲突),那么便会将所要添加的对象与当前位置中(即所谓的“桶”中,实则是数组中的链表元素)的所有对象进行比较,查看这个对象是否已经存在(调用 equals 方法来进行比较)。
如果不重复,那么便添加到桶中(实际上因为 HashSet 是数组链表,数组中的每个元素都是链表的头节点),即添加到当前位置的链表的尾部;

如果重复了则放弃添加。如下图所示
(补充:如果链表的长度达到一定值,出于查询效率的原因,链表会”树化“,转变成一颗搜索树)
在这里插入图片描述

因此上述问题的关键就在于 HashSet 中添加的元素的散列码是否相同,正如前文所说 String 类的 hashCode 方法被重写了,因此一旦字符串的内容是一致的,那么散列码就是一样的,这样便会造成所添加的元素重复,无法重复添加;而反观上面代码中向 HashSet 中添加 Node 节点,Node 中的 hashCode 方法没有重写,因此获取散列码的方式就是返回其对象的存储地址,因此即便 Node 中的 item 都为 “Winters” ,但由于是两个不同的对象,散列码不同因此可以添加成功。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值