一、重写了equals为什么还要重写hashcode

一、为什么重写了equals要重写hashcode?

有这么一个场景,当用户登录时,来了两个user,有name和age,并且还有手机号,手机号相同的我认为是一个用户。那么我们很容易得到以下代码:

import java.util.HashMap;
public class MockLogin {
    static class User {
        public int age;
        public String name;
        public String mobile;

        public User( String mobile,int age, String name) {
            this.age = age;
            this.name = name;
            this.mobile = mobile;
        }
    }

    public static void main(String[] args) {
        //小灰灰登录
        User xhhFirstLogin = new User("123456",21, "xiaohuihui");
        //这里我想记录小灰灰是否登陆过
        HashMap<User,Boolean> map = new HashMap<>();
        map.put(xhhFirstLogin,true);

        //小灰灰第二次登陆来了,如果登陆过,我就把之前登陆信息给他,不再次登陆
        User xhhSecondLogin = new User("123456",21, "xiaohuihui");
        boolean b = map.containsKey(xhhSecondLogin);
        //看是否登陆过
        System.out.println(b?"登陆过":"没有登录");
    }
}

但实际运行结果是:

没有登录

聪明的你一定看出来了,你没有重写equals啊,怎么判断手机号相等是同一个对象,好,那我们重写一下equals:

public class MockLogin {
    static class User {
        public int age;
        public String name;
        public String mobile;

        public User( String mobile,int age, String name) {
            this.age = age;
            this.name = name;
            this.mobile = mobile;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            User user = (User) o;
            return Objects.equals(mobile, user.mobile);
        }
    }

    public static void main(String[] args) {
        //小灰灰登录
        User xhhFirstLogin = new User("123456",21, "xiaohuihui");
        //这里我想记录小灰灰是否登陆过
        HashMap<User,Boolean> map = new HashMap<>();
        map.put(xhhFirstLogin,true);

        //小灰灰第二次登陆来了,如果登陆过,我就把之前登陆信息给他,不再次登陆
        User xhhSecondLogin = new User("123456",21, "xiaohuihui");
        boolean b = map.containsKey(xhhSecondLogin);
        //看是否登陆过
        System.out.println(b?"登陆过":"没有登录");
    }
}

重写完之后的结果:

没有登录

还是没有登录。那么问题究竟出在哪里了呢?既然是containsKey返回的false,我们就去看看containsKey是怎么写的吧

    public boolean containsKey(Object key) {
        return getNode(key) != null;
    }

接着往下找:

    /**
     * Implements Map.get and related methods.
     *
     * @param key the key
     * @return the node, or null if none
     */
    final Node<K,V> getNode(Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & (hash = hash(key))]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

哦吼,这么多代码,关掉你的文章,休息一下。秋冬马爹,其实这里面只有一个行我们是需要注意的:

            
hash = hash(key));//根据key算出来的hash
if (first.hash == hash && //对比第一个节点的hash值
                ((k = first.key) == key || (key != null && key.equals(k)))) //如果key和第一个节点的key相同或者(key不等于空并且相等)
                return first;

我们知道hashmap是一个拉链式的hash结构:
在这里插入图片描述

containsKey是先找到hashcode,然后再来对比是否相等的。那么我们很容易得到猜想,是不是刚才hashcode不一样,导致的containsKey返回了false。我们先来打印一下hashcode:

....
boolean b = map.containsKey(xhhSecondLogin);
System.out.println("fcode = "+xhhFirstLogin.hashCode()+"  scode = "+xhhSecondLogin.hashCode());

看一下执行结果:

fcode = 1704856573  scode = 705927765
没有登录

果然,是hashcode导致了我们containsKey函数的失败。那我们先简单的处理一下,让他们相等:

       public User( String mobile,int age, String name) {
            this.age = age;
            this.name = name;
            this.mobile = mobile;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            User user = (User) o;
            return Objects.equals(mobile, user.mobile);
        }

        @Override
        public int hashCode() {
            return 1;
        }
    }

在user类增加hashcode方法,返回固定值1,我们再跑一下运行结果:

fcode = 1  scode = 1
登陆过

这时真的相等了,当然hashcode = 1这是为了做测试,工程化落地时,我们还是用hashcode标准方法吧:

        @Override
        public int hashCode() {
            return Objects.hash(age, name, mobile);
        }

我们认为,当他所有成员属性一样时,那就是一个对象,再次跑结果:

fcode = -679329960  scode = -679329960
登陆过

技术总结:

  • hashcode是一种快速定位到拉链法hash表数组位置索引的一个值,根据key算出来。

  • hashcode重写主要是为了解决对象在hash表中当key时,equals相等,但是hashcode不相等,导致containsKey错误返回的问题

贴一下最后的源代码:

package com.android;
import java.util.HashMap;
import java.util.Objects;

public class MockLogin {
    static class User {
        public int age;
        public String name;
        public String mobile;

        public User( String mobile,int age, String name) {
            this.age = age;
            this.name = name;
            this.mobile = mobile;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            User user = (User) o;
            return Objects.equals(mobile, user.mobile);
        }

        @Override
        public int hashCode() {
            return Objects.hash(age, name, mobile);
        }
    }

    public static void main(String[] args) {
        //小灰灰登录
        User xhhFirstLogin = new User("123456",21, "xiaohuihui");
        //这里我想记录小灰灰是否登陆过
        HashMap<User,Boolean> map = new HashMap<>();
        map.put(xhhFirstLogin,true);

        //小灰灰第二次登陆来了,如果登陆过,我就把之前登陆信息给他,不再次登陆
        User xhhSecondLogin = new User("123456",21, "xiaohuihui");
        boolean b = map.containsKey(xhhSecondLogin);

        System.out.println("fcode = "+xhhFirstLogin.hashCode()+"  scode = "+xhhSecondLogin.hashCode());

        //看是否登陆过
        System.out.println(b?"登陆过":"没有登录");
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值