A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

hashCode和equals属于Object的方法。

Object中的equals方法的实现:

public boolean equals(Object obj) {
   return (this == obj);
}
Object中的hashCode方法是native的,实现和本地机器相关,当然你也可以重写它:

public native int hashCode();  
下面我们来分几个点介绍下hashCode和equals这对姐妹花。



1. equals是谁?

Object类中的equals方法和==作用是一样的,即比较的是俩个对象在栈内存中存储的内存地址。而String,Integer等一些类是重写了equals方法,才使得equals和==不同,它们比较的是值相不相等。

所以当自己创建类时,自动继承了Object的equals方法,要想实现不同的等于比较,必须重写equals方法。

来看一个简单的例子:

    private static class Coder {

        final String name;
        final String lang;
        final int age;

        Coder(String name, String lang, int age) {
            this.name = name;
            this.lang = lang;
            this.age = age;
        }
    }

    public static void main(String[] args) {
        Coder coder1 = new Coder("kuang", "java", 27);
        Coder coder2 = new Coder("kuang", "java", 27);
        System.out.println("coder1 == coder2: " + (coder1 == coder2)
                + " , coder1.equals(coder2): " + coder1.equals(coder2));
    }
执行输出:coder1 == coder2: false , coder1.equals(coder2): false

此时equals和==都是比较coder1和coder2在栈内存中存储的内存地址。

如果我们重写下equals:

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Coder coder = (Coder) o;
            return age == coder.age &&
                    Objects.equals(name, coder.name) &&
                    Objects.equals(lang, coder.lang);
        }        
执行输出:coder1 == coder2: false , coder1.equals(coder2): true

一般可以用IDE直接重写equals和hashCode方法,比如IntelliJ IDEA:


2. hashCode是谁?

hashCode的作用是获取哈希码,也称为散列码。它实际上是返回一个int整数,这个哈希码的作用是确定该对象在哈希表中的索引位置。

hashCode定义Object中,Java中的任何类都包含有hashCode方法。

虽然,每个Java类都包含hashCode方法。但是,仅仅当创建某个类的散列表时,该类的hashCode才有用,其它情况下如创建类的单个对象,或者创建类的对象数组等hashCode是没有作用的。

上面的散列表指的是:Java集合中本质是散列表的类,如HashMap,Hashtable,HashSet。

也就是说:hashCode在散列表中才有用,在其它情况下没用。在散列表中hashCode的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

还是上面那个例子:

    private static class Coder {

        final String name;
        final String lang;
        final int age;

        Coder(String name, String lang, int age) {
            this.name = name;
            this.lang = lang;
            this.age = age;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Coder coder = (Coder) o;
            return age == coder.age &&
                    Objects.equals(name, coder.name) &&
                    Objects.equals(lang, coder.lang);
        }
    }

    public static void main(String[] args) {
        Coder coder1 = new Coder("kuang", "java", 27);
        Coder coder2 = new Coder("kuang", "java", 27);
        System.out.println("code1 hashCode = " + coder1.hashCode());
        System.out.println("code2 hashCode = " + coder2.hashCode());
        System.out.println("coder1 == coder2: " + (coder1 == coder2)
                + " , coder1.equals(coder2): " + coder1.equals(coder2));
    }
执行输出:
code1 hashCode = 1625635731
code2 hashCode = 1580066828
coder1 == coder2: false , coder1.equals(coder2): true

可以看到coder1.equals(coder2)时,它们的散列码并不相等。





3. equals和hashCode的关系

(1) 我们不会在HashSet,Hashtable,HashMap等这些本质是散列表的数据结构中用到该类。例如,不会创建该类的HashSet集合,这时equals和hashCode是没有关系的。

(2) 我们会在HashSet,Hashtable,HashMap等这些本质是散列表的数据结构中用到该类。例如,会创建该类的HashSet集合,在这种情况下,该类的equals和hashCode是有关系的:

如果两个对象相等,那么它们的hashCode值一定相同。这里的相等是指,通过equals比较两个对象时返回true。
如果两个对象hashCode相等,它们并不一定相等。因为在散列表中hashCode相等,即两个键值对的哈希值相等,并不一定能得出键值对相等。"两个不同的键值对,哈希值相等",这就是哈希冲突。在这种情况下,若要判断两个对象是否相等,除了要覆盖equals之外,也要覆盖hashCode方法,否则equals无效。
还是上面那个例子,只重写了Coder类的equals方法:

    public static void main(String[] args) {
        HashSet<Coder> coders = new HashSet<>();
        coders.add(new Coder("kuang", "java", 27));
        coders.add(new Coder("kuang", "java", 27));
        System.out.println(coders.size());
    }
打印输出coders的大小是2。

我们再重写下Coder类的hashCode方法:

        @Override
        public int hashCode() {
            return Objects.hash(name, lang, age);
        }
打印输出coders的大小是1。





4. 散列表中是如何判断两个对象是否相等的

以上面的HashSet为例,看看它的put方法实现:

if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
先判断hash值是否相等,然后再判断equals是否相等,这样也提高了效率,比如HashSet中已有100个元素,那么第101个元素加入HashSet时,它就要调用100次equals方法,这显然会大大降低效率。

在散列表中,两个对象相等(用equals比较相等),hashCode一定相等,而hashCode相等,两个对象不一定相等(哈希冲突)。

至于散列表如何解决哈希冲突,我在《Java篇 - 并发容器之Hashtable源码分析》文章中有详细讲过。

https://blog.csdn.net/u014294681/article/details/85298342

而对哈希算法感兴趣的同学,可以看看这篇文章《深入理解hashcode和hash算法》

https://blog.csdn.net/qq_38182963/article/details/78940047





5. hashCode引起的内存泄漏

所谓内存泄露就是一个对象占用的一块内存,当这个对象不在被使用时,该内存还没有被收回。本例用于改变了对象hashCode值致使对象没有能从HashSet中删除,从而导致内存泄露。

看下这个例子:

    private static class Point {

        private int x;
        private int y;

        Point(int x, int y) {
            super();
            this.x = x;
            this.y = y;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Point point = (Point) o;
            return x == point.x &&
                    y == point.y;
        }

        @Override
        public int hashCode() {

            return Objects.hash(x, y);
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }

    public static void main(String[] args) {
        HashSet<Point> hashSet = new HashSet<>();
        Point p1 = new Point(3, 3);
        Point p2 = new Point(3, 5);
        hashSet.add(p1);
        hashSet.add(p2);
        p2.setY(7);
        hashSet.remove(p2);
        System.out.println(hashSet.size());
    }
你可能认为打印结果是1,但是运行后结果是2。

当一个对象被存储在HashSet中后,如果修改参与计算hashCode有关的字段,那么修改后的hashCode的值就与一开始存储进来的hashCode的值不同了,这样contains无法通过hashCode找到该元素,所以无法删除。这就告诉我们,当一个对象被存储在HashSet中后,不要修改与计算hashCode有关的字段。
---------------------
【转载,仅作分享,侵删】
作者:况众文
原文:https://blog.csdn.net/u014294681/article/details/85344555
版权声明:本文为博主原创文章,转载请附上博文链接!

2 个回复

倒序浏览
一个人一座城0.0 来自手机 中级黑马 2019-2-15 12:40:53
沙发
看一看。
回复 使用道具 举报
今天也要加油鸭
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马