equals()和==、hashcode()

一、equals()和==

1.以下是Object类中equals()的源码

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

2.以下是Integer类中equals()的源码

public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            //返回当前对象的值与参数对象的值的比较
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

3.以下是String类中equals()的源码

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        //判断参数是否为String类对象,不是直接返回false
        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;
    }

由以上源码我们可以得出:
① equal和==最根本的区别在于equal是一个方法,而==是一个运算符。
② 基本数据类型只能用==来进行比较。在object类中equal()方法就是用==实现的,但是对于,Math、Integer、Double等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法,他们比较的是内容(值)。
③ 在String类中equals()也被重写了,因此字符串比较返回true有两种情况(==:地址内容都一致,equals():内容一致)

题外话:下面代码的运行结果是什么?解释一下为什么会有这些差异。

String s1 = "hello";
String s2 = s1 + ",world";
String s3 = "hello" + ",world";
String s4 = "hello,world";
String s5 = new String("hello,world");

System.out.println(s2.equals(s4)); // true
System.out.println(s2==s4);     // false
System.out.println(s3==s4);     // true
System.out.println(s4==s5);     // false

一般情况下,创建字符串对象有两种方式,一种是字面值创建,一种是通过 new 得方式创建,这两者是不一样的。

如果是字面值创建的方式,如 String s4="hello,world",JVM会先去字符串常量池中寻找有没有“hello,world”这个字符串,若有,则将其地址给 s4;若没有,则先在常量池里创建“hello,world”,然后再把地址给s4;而通过 new 的方式创建对象,则是在堆中创建“hello,world”对象,s5 指向这个对象。还是看图吧,比较直观:

字符串的拼接
String 类被 final 修饰,因此字符串不能修改,当两个字符串相加时,是先生成 StringBuilder 对象,然后通过 append() 的方式将两个字符串拼接,再调用 toString() 方法生成新的字符串对象。所以只考虑 s1 和 s2,他们在 JVM 中是这样的:

但是像 String s3 = "hello" + ",world" 这样直接两个字面值相加的,java文件在编译期间就已经将这条语句做了优化,将其直接变成 "hello,world",等到运行的时候就查找字符串常量池,因此 s3 == s4 返回的结果就为 true。

String、StringBuilder、StringBuffer
String 是常量,且每次需要在原来字符串基础上扩展都需要新建对象,导致速度会稍微慢一点,而且占内存,因此对于需要经常扩展的字符串,可以使用 StringBuilder 和 StringBuffer。但是 StringBuilder 和 StringBuffer 又有区别,即使两者很像,在速度上 StringBuilder 会快一些,因为 StringBuffer 的操作加了 synchronized ,即加了锁,使得操作相对比较慢,就举 append() 为例子,StringBuffer 中此方法的源码如下:

所以,一般在单线程的情况下,可以选择使用 StringBuilder;在多线程的情况下可以选择 StringBuffer .

总结

回到一开始的程序,我们可以大致地画出在 JVM 中的存储: 

二、hashcode()

hashCode()方法给对象返回一个hash code值。

性质:

  • 在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用hashCode()方法,该方法必须始终如一返回同一个integer。
  • 如果两个对象根据equals(Object)方法是相等的,那么调用二者各自的hashCode()方法必须产生同一个integer结果。
  • 并不要求根据equals(java.lang.Object)方法不相等的两个对象,调用二者各自的hashCode()方法必须产生不同的integer结果。[1]


1.以下是Object类中hashcode()的源码,说明是一个本地方法,它的实现是根据本地机器相关的。大量的实践表明,由Object类定义的hashCode()方法对于不同的对象返回不同的integer

public native int hashCode();

2.以下是String类中hashcode()的源码,可以看出字符串的内容相同hashcode也相同。

public int hashCode() {
        int h = hash;   //hash默认值是0
        //字符串的内容相同hashcode也相同
        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;
    }

3.以下是AbstractSet类中hashcode()和equals()的源码
在set集合中,jvm就是根据hashcode(),和equals()这两个方法来判断元素是否重复,如果要重写,建议两个方法一起重写,否则可能会出现安全性问题。

public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }
 public int hashCode() {
        int h = 0;
        Iterator<E> i = iterator();
        while (i.hasNext()) {
            E obj = i.next();
            if (obj != null)
                h += obj.hashCode();
        }
        return h;
    }

4.以下是AbstractList类中hashcode()和equals()的源码

 public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        return !(e1.hasNext() || e2.hasNext());
    }

public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }

参考:
https://mp.weixin.qq.com/s/2rTxX3Co-2bDR16wZE-WwQ
https://www.cnblogs.com/Qian123/p/5703507.html


  1. 两个对象equals()结果相同则两者hashcode()也相同;而两者hashcode()相同,equals()结果不一定相同;因此,在Set中判断元素是否重复的其中一个重要原则就是先判断hashcode,若相同再判断equals()是否为true。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容