
在 Java
中,重写 equals()
必须要重写 hashCode()
,那么为什么?简单来说,两个对象如果相等,则 hashCode 一定也是相同的。两个相等对象比较时,调用 equals()
会返回 true
。但是由于哈希冲突,两个有着相同 hashCode 的对象也不一定相等。因此,覆盖equals()
时也需要覆盖 hashCode()
。
Hash 冲突:Hash 算法并不完美,有可能两个不同的原始值在经过哈希运算后得到同样的结果, 这样就是哈希冲突(碰撞)。
业务逻辑
一般在创建类对应的散列表时,hashCode()
是生效的。举个例子,有这样一个业务场景(源于 ThoughtWork 暑期实习结对编程面试题):
- 每个商品有自己的
ProductCode
,以 “BULK_BUY_2_GET_1” 开头的ProductCode
且Product.name
相同的商品享受买2送1,即三件商品付两件多的价格。 - 购物车中可能存在各种
Product.name
的商品 - 我们规定拥有相同名称和价格的商品为同一件商品
解决方式是创建一个 Map
保存一个 相同 商品的商品数量,创建一个 Set
保存购物车中所含商品的种类。
1 | Map<String, Integer> buy2Get1CartMap = new HashMap<>(); |
在结算时遍历 Set
在 Map
中取出每种商品的数量进行总价计算。
1 | //... |
注意:这里不一定是这个业务逻辑的最佳解决方案,作者只是想借用其讲述
hashCode()
的使用场景
重写比较函数
如果你在这里实现了完整的代码会发现,代码块中的 productSet.add(product);
操作会将同名商品重复添加到 Set 中,这是怎么回事?首先他们的地址不相等这是当然的,但是他们的属性相等,至少,我们规定,同名商品就是相同的商品。这里光是针对 Product.name
和 Product.price
重写 equals()
是没用的,因为不同的地址导致默认比较方法会认为他们是不同的,那么这里就需要我们改写 hashCode()
来改变比较逻辑。
在 Product
对象中重写 equals()
和 hashCode()
:
1 |
|
在 equals()
中我们将判断逻辑变成了比较 Product.name
和 Product.price
属性,这里为了保证比较函数的健壮性,需要对空对象进行特判,具体逻辑在备注中说的很清楚了,就不再赘述了。
重点在于 hashCode()
的改写,变量 prime
保存的是一个作为特征值的奇素数,其用于计算时带来更好的性能(在 Effective Java 48 页,第 3 章中有提到)。另外我们利用两个关键属性,即 Product.name
和 Product.price
计算 hashCode, 算法具体比较复杂,我们这里简单理解为:
对每一个关键域进行计算:$reslut = prime * result + KeyHashCode$
其中 result 初始为一个非零常数,对于关键属性(称之为关键域,即公式中的 KeyHashCode)的 hashCode 计算规则如下:
类型 | 计算方式 |
---|---|
byte, char, short, int | (int)obj |
boolean | obj ? 1 : 0 |
long | (int)(obj^(obj>>>32)) |
float | Float.floatToIntBits(obj) |
double | Double.doubleToLongBits(obj) 再使用 long 的方式计算 |
对象引用 | (obj == null) ? 0 : obj.hashCode() |
Array | 对每个元素单独处理 或者 使用 Arrays.hashCode() |
由此我们重写了 Product
的比较函数,这里再使用 Set 去重是,就能按照业务逻辑正常工作量。
注意:作者对于
hashCode()
的重写说不上熟练,只是通过这个实例正确实现了重写的效果,实际操作中如有问题烦请及时评论。
你,学废了吗?
- Post title:关于 hashCode 与 equals
- Post author:Lynch Lee
- Create time:2021-04-23 20:06:59
- Post link:https://neeomaclynch.github.io/2021/04/23/关于-hashCode-与-equals/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.