代码细节---Java.HashSet用法

这是我写的第一篇关于语法以及Java常用类用法的博客。

我之所以突然想写这篇博客是因为我今天看到了一道非常好的题目:

HashSet set = new HashSet();
Person p1 = new Person(1001,"AA");
Person p1 = new Person(1002,"BB");

set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);
System.out.println(set);
set.add(new Person(1001,"CC"));
System.out.println(set);
set.add(new Person(1001,"AA"));
System.out.println(set);

其中Person类重写了hashCode()和equal()方法。

问最终打印结果是什么。

 

这道题我可以说是涵盖了HashSet的大部分知识点,并且完全涉及到了HashSet的底层原理,是一道难度比较大的题目。

首先我们要知道HashSet的原理是什么。

这里就要用到数据结构的哈希值运算以及拉链法。

Person类重写了Object的hashCode()和equal()方法

那么每个Person对象的哈希值就确定下来了。

这里我还要讲一下HashSet的底层是怎么样的:

HashSet底层需要一个数组和链表,通过加入的对象的哈希值散射到 [ 0 , 数组长度 - 1 ] 的范围上得到一个索引值,插入到对应数组下标中,

如果那个位置有元素或者有链表了,就一一比较( 用equal() ),如果比较完了equal()返回false,说明不相同,就插入到那个位置或者插入到对应的链表位置,

如果返回true则说明这个元素和它相同,违背了Set集合的不重复性这条原则,则不插入。

 

我们再回到这个题目,题目中首先定义了一个HashSet集合,并且插入了两个Person对象,然后关键的一点来了:

p1.name = "CC";
set.remove(p1);
System.out.println(set);

这行代码调用之后打印啥呢?我来画一张图助于理解:

p1 p2 放置的位置都是根据这两个对象的哈希值确定的,这个时候我们把p1的name字段修改,

这个时候注意:p1本质是一个引用,p1指向的对象的name字段值改了,在set容器中存放的也是这个引用。

然后调用Collection接口中的remove方法,那么HashSet也是根据哈希值来删除这个元素的,

但是在name这个字段值修改了之后大概率找不到这个元素,因为字段修改了,哈希值也就变了,我们是根据哈希值去移除这个元素的,

现在找不到,remove就失效了,无法移除了。

所以最终打印还是p1,p2元素。

set.add(new Person(1001,"CC"));
System.out.println(set);

再看这段代码,其实这和之前的道理是一样的,remove失效了,因为哈希值变了,那么现在new的这个对象哈希值的散射值在HashSet底层数组中的索引位置还是没有元素(大概率)

所以其实add成功了,所以这个时候打印了3个元素。

set.add(new Person(1001,"AA"));
System.out.println(set);

这里的哈希值是存在的,就是之前的那个p1的哈希值,但是有了哈希值还要调用equal()方法,这里equal()方法返回false了,插入仍然成功,所以打印的set就有4个元素。

这道题就过了,还是一道非常好的题目,很有价值做的题目。

posted @ 2021-09-08 17:42  Apak陈柏宇  阅读(143)  评论(0编辑  收藏  举报