hashset去重原理_HashSet初探

我之前的toString文章里已经写了,创建一个自定义类Person。 然后创建对象

Person p = new Person()

打印这个p就是打印的是Integer.toHexString(hashCode() )

就是打印hashCode() 的16进制。

然后我们知道set是无序的,这个编程语言里大多都是无序的吧, ,java ,js,c++,python里的set都是无序的。

hashset的底层是哈希表也叫散列表。

哈希表是数组+链表,hashcode的值对数组长度取模,结果就是index。假如有个hashcode的值是25,数组长度是6,那么该对象存在数组的第1个角标上 因为25对6的余数是1。

哈表里存的就是对象的地址(地址值其实就是hashcode,暂时不说进制转换了)对数组取模,但是存的时候,不是按照集合存的顺序来存地址的,而是地址之间有排序,所以hashset是无序的,就是add了很多对象的地址,然后哈希表里的顺序,并不是你add的顺序,而是那些地址值在哈希表里本身的顺序,按照哈希值来存的。那取的时候,怎么取呢? 也是按照哈希表里哈希值的顺序来取。

然后我们给自定义类Person里重写一个hashcode方法。renturn 666

现在对象返回的hashcode都一样了,那么在哈希表里怎么存,重复了呀,那么就在该数组的下标下进行顺延,长度满8的时候,转成红黑树,所以要求重写hashcode的时候,尽量不要重复,为了性能,尽量满足均匀分布,不要最后成了高斯分布就凉了,那样的话一个下标下可以顺延有n多个,找到下标之后还要遍历,效率太低

来考虑一下,自定义Person类。如果姓名年龄都相同的话,就视为相同的元素,则不能存到集合中,首先hashset就是不重复的,现在问题是定义一个Person类,写一个定义姓名年龄都一样的equals方法用来判断是不是同一个人,然后再写一个hashset对象直接add person类的对象靠谱吗?靠不靠谱,来看代码

person还是这个person,get方法就不截图了,set是无序的,不重复的,现在添加自定义方法,如果姓名和年龄都相同的话,就是相同的两个元素,重复了,然后不能进来。

ecce3457c173d24f0896ad63468ec153.png

b8289c89319771f5b470879d3b28d66d.png

运行结果是下图,为什么没有去重?而且equals()方法里的输出语句都没有打印出来。说明根本没有走equals方法。

0e19383cbfbabd1fb0728a733ee1d3c9.png

hashSet一定是先比较hashCode然后才走equals。 如果hashCode不一样,那么都存了,如果hashCode一样,那么才走equals。所以上图光重写了equals ,根本不走equlas的,因为每一个person对象的hashcode都不一样。所以都存了。

这块,首先hashset会去哈希表里找,哈希表里存的就是对象的hashcode值经过16进制转换的,那你想一下,凡是new出现的对象的地址值肯定都是不一样的,因为他们的hashcode都是用了哈希算法实现的也是不一样的,(具体是移位操作,2的5次方-1 = 31 是一个质数,保证了乘出来的数,不会越出存储的界限,47,37也都是质数,最后拒绝了,选了31,这样下来hashcode重复的也不多,也保证了存储越界的问题。

那么哈希表里保存的那些new出来的东西地址值,而那些new出来的东西的地址值都不一样,说明元素没有重复,那说明new出来的东西,在hashset里都是可以存的,而且都是唯一的,更别说走equals了,地址值都不一样,比较啥equals啊,地址值不一样,那完全是两个事物啊。那这样怎么办,我们重写一个hashcode方法,让他返回一个666吧,这样hashset里存的对象的地址值全是666了,然后在这个基础上元素进来了 都挤在地址值为666这个位置上,然后大家往下顺延。因为地址值一样,所以往下顺延。hashset比较的时候是先比较hashcode值,hashcode值如果不一样,那就可以存。如果hashcode值一样,那就比较具体的equals方法。而我们重写的equals方法就是比较姓名和年龄的。然后大家都挤在一个地方,每进来一个元素都要走一下equals方法,和其他元素进行比较,如果自定义的equals方法比较出来是重复的了,例如名字和年龄相同的haul,那么就添加不进来。这样一来就可以避免同姓名同年龄的人存进来了。

话说回来,这样定义hashcode方法是不是很傻,因为所有对象都挤在了一个地方,那有没有更好的方法呢,有,可以返回一个name的hashcod()+age就行了。因为name是字符串,是有重写hashcode方法的。代码我觉得不用再写了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值