1、equals和==的区别
除了基本类型,equals和==比较的都是对象的地址值。如下为Object的equals方法
public boolean equals(Object obj) {
return (this == obj);
}
对于基本数据类型,equals和==比较的都是值,数值相等就相等。
基本数据的包装类型, 包装类都重写了equals方法,如下为Integer的equals方法。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
所以包装类型的equals比较的是对象的值,如果值相等,equals就返回true。
==比较的时候:除了Float和Double,都有对应的缓存
包装类型 | 缓存对象 |
Boolean | true,false |
Character | 0-127 |
Byte | -128-127 |
Short | -128-127 |
Integer | -128-127 |
Long | -128-127 |
Float | 无缓存值 |
Double | 无缓存值 |
对于所有包装类型,==在缓存对象范围内,如果数值相等==返回true,缓存范围之外==返回false
String类 对于String,重写了equals方法,比较的是字符串的值。
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
==比较,因为常量池的存在,有时候会在常量池共用对象的引用,具体参考Java从入门到放弃(一)String类
其他引用类型 如果重写了equals方法,则按照重写的规则进行对比,如果没有重写,则依据其父类的equals方法,如果一个类没有继承任何类,则默认基础Object类,所以equals和==一样比较的是地址值。
public class ClassA {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ClassB {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassB)) return false;
ClassB classB = (ClassB) o;
return age == classB.age &&
Objects.equals(name, classB.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
public class Main {
public static void main(String[] args) {
ClassB classB1 = new ClassB();
ClassB classB2 = new ClassB();
ClassA classA1 = new ClassA();
ClassA classA2 = new ClassA();
System.out.println(classB1.equals(classB2)); //true
System.out.println(classA1.equals(classA2)); //false
}
}
2、equals和hacode
equals方法:JDK中说明了实现equals()方法应该遵守的约定:
(1)自反性:x.equals(x)必须返回true。
(2)对称性:x.equals(y)与y.equals(x)的返回值必须相等。
(3)传递性:x.equals(y)为true,y.equals(z)也为true,那么x.equals(z)必须为true。
(4)一致性:如果对象x和y在equals()中使用的信息都没有改变,那么x.equals(y)值始终不变。
(5)非null:x不是null,y为null,则x.equals(y)必须为false
hashcoed方法:
public native int hashCode();
可以看出,hashCode()是一个native方法,而且返回值类型是整形;实际上,该native方法将对象在内存中的地址作为哈希码返回,可以保证不同对象的返回值不同。
与equals()方法类似,hashCode()方法可以被重写。JDK中对hashCode()方法的作用,以及实现时的注意事项做了说明:
(1)如果对象在equals()中使用的信息都没有改变,那么hashCode()值始终不变。
(2)如果两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等。
(3)如果两个对象使用equals()方法判断为不相等,则不要求hashCode()也必须不相等;
String的hashcode方法:
public static int hashCode(byte[] value) {
int h = 0;
for (byte v : value) {
h = 31 * h + (v & 0xff);
}
return h;
}
String的hashcode方法使用了字符串本身作了处理后返回一个int数,这是为了尽量使不同的对象拥有不同的hashcode值,以提高在hash码在哈希表中的效率。如hashmap和hashset等。所以在重写hashcode的时候要合理重写hashcode方法,如果会很多对象的hashcode值一样,会导致hashmap的效率大大降低。