查看: 251|回复: 0
打印 上一主题 下一主题

[收藏] Java重写对象的equals方法和hashCode方法

[复制链接]
  • TA的每日心情
    开心
    2017-7-5 20:14
  • 签到天数: 4 天

    [LV.2]偶尔看看I

    1702

    主题

    1711

    帖子

    7386

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    7386
    跳转到指定楼层
    楼主
    发表于 2019-8-27 18:45:43 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    一 为什么要重写 equals 方法
    因为不重写 equals 方法,执行 user1.equals(user2) 比较的就是两个对象的地址(即 user1 == user2),肯定是不相等的,见 Object 源码:
    1. if (this == anObject) {
    2. ? ?? ???return true;
    3. }
    复制代码

    二 为什么要重写 hashCode 方法
    既然比较两个对象是否相等,使用的是 equals 方法,那么只要重写了 equals 方法就好了,干嘛又要重写 hashCode 方法呢?
    其实当 equals 方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。那这又是为什么呢?看看下面这个例子就懂了。
    User 对象的 hashCode 方法如下,没有重写父类的 hashCode 方法
    1. @Override
    2. public int hashCode() {
    3. ? ?? ???return super.hashCode();
    4. }
    复制代码
    显然,这不是我们要的结果,我们是希望两个对象如果相等,那么在使用 hashSet 存储时也能认为这两个对象相等。
    通过看 hashSet 的 add 方法能够得知 add 方法里面使用了对象的 hashCode 方法来判断,所以我们需要重写 hashCode 方法来达到我们想要的效果。
    将 hashCode 方法重写后,执行上面结果为
    1. @Override
    2. public int hashCode() {
    3. ? ?? ???int result = 17;
    4. ? ?? ???result = 31 * result + (name == null ? 0 : name.hashCode());
    5. ? ?? ???result = 31 * result + (age == null ? 0 : age.hashCode());
    6. ? ?? ???return result;
    7. }
    复制代码
    所以:hashCode 是用于散列数据的快速存取,如利用 HashSet/HashMap/Hashtable 类来存储数据时,都会根据存储对象的 hashCode 值来进行判断是否相同的。

    三 如何重写 hashCode
    生成一个 int 类型的变量 result,并且初始化一个值,比如17
    对类中每一个重要字段,也就是影响对象的值的字段,也就是 equals 方法里有比较的字段,进行以下操作:a. 计算这个字段的值 filedHashValue = filed.hashCode(); b. 执行 result = 31 * result + filedHashValue;

    四 为什么要使用 31
    原因一:更少的乘积结果冲突
    31是质子数中一个“不大不小”的存在,如果你使用的是一个如2的较小质数,那么得出的乘积会在一个很小的范围,很容易造成哈希值的冲突。而如果选择一个100以上的质数,得出的哈希值会超出int的最大范围,这两种都不合适。而如果对超过 50,000 个英文单词(由两个不同版本的 Unix 字典合并而成)进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个(国外大神做的测试),那么这几个数就被作为生成hashCode值得备选乘数了。
    所以从 31,33,37,39 等中间选择了 31 的原因看原因二。
    原因二:31 可以被 JVM 优化
    JVM里最有效的计算方式就是进行位运算了:
    * 左移 << : 左边的最高位丢弃,右边补全0(把 << 左边的数据*2的移动次幂)。
    * 右移 >> : 把>>左边的数据/2的移动次幂。
    * 无符号右移 >>> : 无论最高位是0还是1,左边补齐0。   
    所以 : 31 * i = (i << 5) - i(左边??31*2=62,右边? ?2*2^5-2=62) - 两边相等,JVM就可以高效的进行计算啦。。。

    五 代码示例
    5.1 User.java
    1. package com.zydgbbs.entity;

    2. public class User {
    3. ? ? private String id;
    4. ? ? private String name;
    5. ? ? private String age;

    6. ? ? public String getId() {
    7. ? ?? ???return id;
    8. ? ? }

    9. ? ? public void setId(String id) {
    10. ? ?? ???this.id = id;
    11. ? ? }

    12. ? ? public String getName() {
    13. ? ?? ???return name;
    14. ? ? }

    15. ? ? public void setName(String name) {
    16. ? ?? ???this.name = name;
    17. ? ? }

    18. ? ? public String getAge() {
    19. ? ?? ???return age;
    20. ? ? }

    21. ? ? public void setAge(String age) {
    22. ? ?? ???this.age = age;
    23. ? ? }
    24. ? ? @Override
    25. ? ? public String toString() {
    26. ? ?? ???return this.id + " " + this.name + " " + this.age;
    27. ? ? }

    28. ? ? public User(String id, String name, String age) {
    29. ? ?? ???this.id = id;
    30. ? ?? ???this.name = name;
    31. ? ?? ???this.age = age;
    32. ? ? }

    33. ? ? @Override
    34. ? ? public boolean equals(Object obj) {
    35. ? ?? ???if(this == obj){
    36. ? ?? ?? ?? ?return true;//地址相等
    37. ? ?? ???}

    38. ? ?? ???if(obj == null){
    39. ? ?? ?? ?? ?return false;//非空性:对于任意非空引用x,x.equals(null)应该返回false。
    40. ? ?? ???}

    41. ? ?? ???if(obj instanceof User){
    42. ? ?? ?? ?? ?User other = (User) obj;
    43. ? ?? ?? ?? ?//需要比较的字段相等,则这两个对象相等
    44. ? ?? ?? ?? ?if(equalsStr(this.name, other.name)
    45. ? ?? ?? ?? ?? ?? ???&& equalsStr(this.age, other.age)){
    46. ? ?? ?? ?? ?? ? return true;
    47. ? ?? ?? ?? ?}
    48. ? ?? ???}

    49. ? ?? ???return false;
    50. ? ? }

    51. ? ? private boolean equalsStr(String str1, String str2){
    52. ? ?? ???if(isEmpty(str1) && isEmpty(str2)){
    53. ? ?? ?? ?? ?return true;
    54. ? ?? ???}
    55. ? ?? ???if(!isEmpty(str1) && str1.equals(str2)){
    56. ? ?? ?? ?? ?return true;
    57. ? ?? ???}
    58. ? ?? ???return false;
    59. ? ? }

    60. ? ? private boolean isEmpty(Object str){
    61. ? ?? ???return str == null || "".equals(str);
    62. ? ? }

    63. ? ? @Override
    64. ? ? public int hashCode() {
    65. ? ?? ???//return super.hashCode();
    66. ? ?? ???int result = 17;
    67. ? ?? ???result = 31 * result + (name == null ? 0 : name.hashCode());
    68. ? ?? ???result = 31 * result + (age == null ? 0 : age.hashCode());
    69. ? ?? ???return result;
    70. ? ? }

    71. }
    复制代码
    5.2 测试类EqualsObjTest.java
    1. package com.zydgbbs.test;

    2. import com.zydgbbs.entity.User;

    3. import java.util.HashMap;
    4. import java.util.HashSet;
    5. import java.util.Set;

    6. public class EqualsObjTest {
    7. ? ? public static void main(String[] args) {
    8. ? ?? ???/* Case1
    9. ? ?? ???User user1 = new User("1", "xiaohua", "14");
    10. ? ?? ???User user2 = new User("2", "xiaohua", "14");
    11. ? ?? ???System.out.println((user1.equals(user2)));
    12. ? ?? ???*/

    13. ? ?? ???//Case2
    14. ? ?? ???User user1 = new User("1", "xiaohua", "14");
    15. ? ?? ???User user2 = new User("2", "xiaohua", "14");
    16. ? ?? ???Set userSet = new HashSet<>();
    17. ? ?? ???userSet.add(user1);
    18. ? ?? ???userSet.add(user2);
    19. ? ?? ???System.out.println((user1.equals(user2)));
    20. ? ?? ???System.out.println(user1.hashCode() == user2.hashCode());
    21. ? ?? ???System.out.println(userSet);

    22. ? ? }
    23. }
    复制代码


    资源帝国 - 论坛版权1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与资源帝国享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和资源帝国的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、资源帝国管理员和版主有权不事先通知发贴者而删除本文

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|资源帝国 ( 皖ICP备14009953号 )?

    GMT+8, 2019-10-3 03:50 , Processed in 0.057803 second(s), 28 queries .

    Powered by Discuz! X3.2

    ? 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表