equals与hashCode方法详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33696779/article/details/70182465

一、在讲解之前,我们先来看看Java jdk源码里边的一些关于equals方法
1.这是Java基类Object里边的equals和hashCode方法,特殊的是这里比较的是两个对象是否相等;

public native int hashCode();
public boolean equals(Object obj) {
        return (this == obj);
    }

2.我们再来看看它下边的一些子类从写后的hashCode 和equals方法

①String类

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
 public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

②HashMap

 class Node<K,V> implements Map.Entry<K,V> {
        //其他代码省略
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

我们就举两个列子吧,通过上边的代码我们可以发现重写后的equals到最后总是在比较两个对象里边的内容是否相等
一般情况下,只要有比较值相等的地方就一定有equals与hashCode。

**二、接下来我们详细讲解一下hashCode与equals方法**


①我们定义一个User类,不要重写equals与hashCode方法:
package equals;

public class User {
    private String userName;
    private String userpass;

    public User(String userName, String userpass) {
        super();
        this.userName = userName;
        this.userpass = userpass;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getUserpass() {
        return userpass;
    }
    public void setUserpass(String userpass) {
        this.userpass = userpass;
    }

}

我们写一个测试方法先测试一下:(直接贴图了)
这里写图片描述
看一下结果:
这里写图片描述
我们可以看到在没有重写equals方法的时候,equals比较返回了false,还记得我们上边所说的Object’里边的equals比较的是两个对象是否相等,User没有从写相应的方法,所以说测试代码直接调用父类的方法进行比较,返回结果为false,相应的Set的size为2

②我们重写一下equals,不重写hashCode(给出代码片段)

@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (userName == null) {
            if (other.userName != null)
                return false;
        } else if (!userName.equals(other.userName))
            return false;
        if (userpass == null) {
            if (other.userpass != null)
                return false;
        } else if (!userpass.equals(other.userpass))
            return false;
        return true;
    }

好了,已经重写完毕,我们运行下上边测试代码,看结果:
这里写图片描述
equals方法返回了true,因为此时它调用自己的equals方法,所以比较对象的值是否相等,但是此时的Set里边的值还是两个(Set无序不可重复集合),在下一层究其原因:

③我们来重写hashCode与equals两个方法(给出代码片段):

@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((userName == null) ? 0 : userName.hashCode());
        result = prime * result
                + ((userpass == null) ? 0 : userpass.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (userName == null) {
            if (other.userName != null)
                return false;
        } else if (!userName.equals(other.userName))
            return false;
        if (userpass == null) {
            if (other.userpass != null)
                return false;
        } else if (!userpass.equals(other.userpass))
            return false;
        return true;
    }

我们来在此运行测试代码看结果:
这里写图片描述

此时Set的大小已经变成了1,综合前一个案列,请注意,这里我们重写了hashCode方法,因此它比较了hashCode与equals两个方法,你们先自己体会一下,我们在下一层,做其他测试。

④这次我们重写hashCode方法而不重写equals方法:

@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((userName == null) ? 0 : userName.hashCode());
        result = prime * result
                + ((userpass == null) ? 0 : userpass.hashCode());
        return result;
    }

Ok,我们来测试运行一下:
这里写图片描述
此时的Set’里边的数据又变为了2,,在这里我们没有重写equals方法,所以equals方法返回的是false

⑤我们在来测试一个案列,测试代码如下:

public class Test {
    public static void main(String[] args) {
        //做一下简单测试,就不写泛型了,重点在理解equals与hashCode
        ArrayList arrayList=new ArrayList();
                    Set set=new HashSet();
        User user1=new User("张三", "asas");
        User user2=new User("张三","asas" );
        User user3=new User("啊哈哈","asas" );
        User user4=new User("asas","asas" );
        arrayList.add(user1);
        arrayList.add(user2);
        set.add(user1);
        set.add(user2);
        set.add(user3);
        set.add(user4);
        System.out.println(user1==user2);
        System.out.println(user1.equals(user2));
        System.out.println(arrayList.size());
        System.out.println(set.size());
        /**
         * 我们来改变一下user4对象属性的值
         */
        user4.setUserName("hahah");
        //修改完之后尝试去删除
        set.remove(user4);
        /**
         * 打印一下set集合大小
         */
        System.out.println(set.size());
    }

}

看一下运行结果:
这里写图片描述
我们看到Set的大小并没有变,主要是因为我们这里在将user4加入到Set集合中之后,在将其对应的属相的值进行修改在去删除的,结果没有删除成功。(原因:由于user4修改的username参与了hashCode的散列算法的计算,参与生成hash 码,因此将对应属性值修改后,对应的hash码被改变,导致要删除的对象没有被找到,所以说,我们在将 对象加入到集合中之后,一般不要修改其对应的属性值)

三、总结
为了解释以上案列情况,先给大家来一张流程图:这里所判断的hashCode与equals都是在判断当前集合是否含有相应的hash码与相应的值
这里写图片描述
通过流程图我们可以看出:
一般像集合(不可重复集合)里边加入数据时,它先会判断集合里边有没有相等的hashCode值,没有就直接加入集合;
如果有的话,他就会去判断集合里边是否有相对应的值,如果没有的话则加入集合,有的话则直接丢弃;
再给大家说一条(equals比较返回为true,则对应的hashCode一定相等,反之则不然,比如HashMap)
在上边的代码中我们看到,当只重写equals方法时,Set集合大小为2,加入了两个有相同值得对象(一般来说Set集合大小应该 为1),这是由于没有重写HashCode而导致的,由于在Set里边加入一个值时,先寻找相同的hash值,如果没有,则直接加入,这里就是由于没有重写hasdCode方法导致产生不同hash值,在集合中没有对应的hash值时直接将对象丢入set集合(HashMap集合也有类似的算法,大家可以去看看源码)
大家根据以上的总结可对代码在此进行回顾,相信已经说得很清楚了。这也更清楚的让我们知道,为何在重写equals方法时要重写hashCode。只不过hashCode方法的重写一般用于有集合对象参与时才使用。

猜你喜欢

转载自blog.csdn.net/qq_33696779/article/details/70182465
今日推荐