Java中的==, equals()及hashCode()

Java中的==, equals()及hashCode()

言兼原创,欢迎转载,转载请注明出处:http://blog.csdn.net/chengqianyun2002

1. 操作符==

操作符==被用来判定两个基本类型或对象是否相等。对于基本类型和对象来说,==有完全不同的意义。

1). 用==来比较基本类型

对于两个基本类型变量来说,==比较的是两个变量的值是否相等。看下面一段简单的测试代码。

    public static void compareBasicType() {
        int a = 10;
        int b = 10;
        short c = 10;
        long d = 10;
        
        System.out.println(a == b); //output: true
        System.out.println(a == c); //output: true
        System.out.println(a == d); //output: true
    }

上面这段测试代码中,a, b, c, d四个变量在栈上分别有自己的存储空间,四个存储空间的大小也并不都相同,但是由于它们存储的值都为10,所以最后三个比较的结果都为true。

2). 用==来比较对象

对于两个对象来说,==比较的是两个对象的引用的值,即比较两个引用是否指向同一个对象。Java对象本身存在于堆中,程序员不能直接操纵对象本身,而需要借助于引用来操纵对象。引用的值就是其所引用的对象的地址,当然Java中对象的地址是对程序员隐藏的,如果你有C/C++编程经验的话,应该很容易理解这个概念。

    public static void compareObjects() {
        Object obj1 = new Object();
        Object obj2 = new Object();
        System.out.println(obj1 == obj2); //output: false
        
        String str1 = new String("123");
        String str2 = new String("123");
        System.out.println(str1 == str2); //output: false   
        
        str2 = str1;     
        System.out.println(str1 == str2); //output: true 
    }

上面这段测试代码中,前两个判断的输出均为false,即使str1和str2有相同的内容,两者比较依然不等,这是因为str1和str2指向的是不同的String对象。在第三个判断中,我们使str2同样指向str1所指向的对象,于是两者被判定为相等了。

另外,值得指出的是,Java不充许操作符重写(Overwrite),所以对于任何类型的对象来说,==的意义是相同的。在这一点上,equals()就不同了,见下文解释。

2. equals()

1). 概述

equals()可以用来比较两个对象,基本类型比较无法使用equals()。

Java在Object类中提供了equals()的实现,由于Java的单根继承特性,你可以在任何类中调用equals()方法。另一方面,由于equals()可能被子类重写(override),所以不同的类的equals()方法可能有所不同。当然,子类也不应该随意地实现equals()方法,Java为equals()方法规定了一些需要遵守的共同约定,这在JDK文档里面有很详细的描述,摘抄于下:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x andy, x.equals(y) should return true if and only ify.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true andy.equals(z) returns true, then x.equals(z) should returntrue.
  • It is consistent: for any non-null reference values x andy, multiple invocations of x.equals(y) consistently returntrue or consistently return false, provided no information used inequals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should returnfalse.

虽然,不同的类的equals()实现可能有所不同,但Object类提供的equals()实现依然值得我们来研究一下。当你没有重写这个方法的时候,调用的可能就是来自Object类的equals()方法。Object提供的equals()实现非常简单,它比较的就是两个引用的值,也就是和操作符==相同。

    public static void compareObjectsWithEquals() {
        Object obj1 = new Object();
        Object obj2 = new Object();
        System.out.println(obj1.equals(obj2)); //output: false
        
        obj1 = obj2;
        System.out.println(obj1.equals(obj2)); //output: true        
    }

2). 关于String的比较

String稍微有一点点的特别,但依然符合上述规则,你需要做的仅仅是弄懂一些细节。先来看一段测试代码。

    public static void compareStrings() {
        String str1 = "123";
        String str2 = "123";
        String str3 = new String("123");
        
        System.out.println(str1 == str2); //output: true
        System.out.println(str1 == str3); //output: false
        System.out.println(str1.equals(str2)); //output: true
        System.out.println(str1.equals(str3)); //output: true
    }

对于上面的输出是否有些疑惑?事实上它依然很简单,来看看String的细节。

首先,Java中的String是一个不可修改的常量对象,Java中String维护有一个自己的常量String池。当你用String str1 = "123"这种方式创建一个String对象时,String类会查找常量池是否已存在同样的对象,如果存在则返回这个对象(因为String对象是不可变的,可以允许多个引用指向同一个对象),否则在池中创建这个对象并返回。所以,str1 == str2的结果就是true了,因为它们根本指向同一个对象。

其次,new String("123")这种方式,明确地告诉String类,我需要一个新的对象,因此它最终得到了一个新的对象。显然,str1 == str3的结果为false。

再次,String中equals()方法被override为比较String对象的内容。str1, str2及str3都具有相同的内容,所以后两个比较都为true。

最后,再看一段简单修改过的代码如下。新的创建方式使得str3同样指向了String同量池中的同一个对象,于是所有比较皆为true。事实上,这是合理的,因为str1, str2, str3指向相同的不可修改的对象,没有必要存在三份拷贝。

    public static void compareStrings() {
        String str1 = "123";
        String str2 = "123";
        String str3 = new String("123").intern();
        
        System.out.println(str1 == str2); //output: true
        System.out.println(str1 == str3); //output: true
        System.out.println(str1.equals(str2)); //output: true
        System.out.println(str1.equals(str3)); //output: true
    }

3. hashCode()

1). 概述

hashCode()方法的作用是用来帮助HashMap,HashSet等类实现hahs table.

hashCode()同样是Object提供的一个方法,它同样可以被重写(Overwrite)。当然,子类同样不应该随意地重写这个方法。关于这个方法的实现,JDK文档给出的约定如下:

The general contract of hashCode is:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, thehashCode method must consistently return the same integer, provided no information used inequals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling thehashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

对于上述约定,我们应该明白几点:

        1. 一个对象的hashCode()返回值在一个Java Application的生命周期内应保持不变;

        2. 两个相等(equals)的对象的hashCode值必须相等;

        3. 两个不相等(!equals)的对象的hashCode值并不是一定不等,但保持不等可提高hash table的查找效率;

2). Object类实现

hashCode值一定是与对象的存储地址相关的一个值吗?不是的。Java并没有规定hashCode()的实现。只是Object类中hashCode()的实现是将对象的存储地址转换为一个int值并返回这个int值。你当然可以有自己的实现,但应该遵守前面所提及的约定。


言兼于2015-01-04






你可能感兴趣的:(Java世界)