- "==" 比较的是两个对象的引用(内存地址)是否相同,也用来比较两个基本数据类型的变量值是否相等。
- equals() 比较的是两个对象的值(内容)是否相同。
二、==和equals()的区别
- 对于==:在简单类型中(int等),这能使用该方法进行比较,这种类型没有equals方法,int的值是存在栈中的,==比较的是栈的内容是否相同。在String类型中,比较特殊,用String=“****”;这种进行赋值时,两个相同的值用==比较也是相同的。但是用new String(),赋值就不相同。说明String=“”时,java会检查在堆中是否由相同的值,如果有,把新对象的地址也同老对象的地址赋为相同,因此==比较会相同(“****”存储在常量区内存中)。但是new String()开辟的就是两个栈,因此用==比较不会相同。对于包装类,如Integer num=127;时,进行自动装箱操作。如果数值在-128-127会有缓存,此时==是相同的;如果数值不在-128~127之间,则==不相同。
- 对于equals:当时String类型或者是包装类(如Integer),比较的就是堆中的值。对于用户自定义的普通类,equals比较的内存的首地址,这时候和==是一样的,即比较两边指向的是不是同一个对象。
三、为什么重写了equals方法时,都要进而重写Hashcode方法呢?
原因如下:当equals此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致。
四、如何重写equals()方法?
package com.study; import java.util.Date; import java.util.Objects; public class Employee { private String name; private double salary; private Date hireDay; public Employee(String name, double salary, Date hireDay) { this.name = name; this.salary = salary; this.hireDay = hireDay; } public Employee() { } /** * 重写equals()方法 * * @param obj * @return */ @Override public boolean equals(Object obj) { // 如果为同一对象的不同引用,则相同 if (this == obj) { return true; } // 如果传入的对象为空,则返回false if (obj == null) { return false; } // 如果两者属于不同的类型,不能相等 if (getClass() != obj.getClass()) { return false; } // 类型相同, 比较内容是否相同 Employee other = (Employee) obj; return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay); } } /** * 测试类 */ class Test { public static void main(String[] args) { Date date = new Date(); Employee e1 = new Employee("zhangsan", 100, date); Employee e2 = new Employee("zhangsan", 100, date); System.out.println(e1.equals(e2)); } }
五、重写hashCode()方法
1、推荐阅读
2、如何重写hashcode方法;
- String对象和Bigdecimal对象已经重写了hashcode方法,这些类型的值可以直接用于重写hashcode方法;
- result = 31 *result + (dishCode !=null ?dishCode.hashCode() : 0);,这里面为啥用个31来计算,而且很多人都是这么写的,这是因为31是个神奇的数字,任何数n*31都可以被jvm优化为(n<<5)-n,移位和减法的操作效率比乘法的操作效率高很多。
3、这边再拷贝下别人说的比较经典的总结:
Google首席Java架构师Joshua Bloch在他的著作《Effective Java》中提出了一种简单通用的hashCode算法
1. 初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;
2. 选取equals方法中用于比较的所有域,然后针对每个域的属性进行计算:
(1) 如果是boolean值,则计算f ? 1:0
(2) 如果是byte\char\short\int,则计算(int)f
(3) 如果是long值,则计算(int)(f ^ (f >>> 32))
(4) 如果是float值,则计算Float.floatToIntBits(f)
(5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int
(6)如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。 否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode值为0
(7)如果是数组,那么需要为每个元素当做单独的域来处理。如果你使用的是1.5及以上版本的JDK,那么没必要自己去 重新遍历一遍数组,java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上,
4、关于hashcode,我们一定要知道一个口诀:
- hashcode相等,两个对象不一定相等,需要通过equals方法进一步判断;
- hashcode不相等,两个对象一定不相等;
- equals方法为true,则hashcode肯定一样;
- equals方法为false,则hashcode不一定不一样;