Java里面equals是Obect类里面定义的方法,今天我们就来研究一下equals的作用。equals的作用
Java里的equals是用来判断两个对象之间是否相等。
equals需要满足下列条件:
自反性:对于非空a,a.equals(a)总是为true
对称性:对于非空的a、b,如果a.equals(b)为true,则b.equals(a)也为true
传递性:对于非空的a、b、c,如果a.equals(b)为true,b.equals(c)为true,则a.equals(c)也为true
一致性:对于非空的a、b,只要a、b未发生改变,a.equals(b)总是返回true或者false
非空性:对于非空的a,a.equals(null)一定为false
我们来看下Obejct的equals实现:
public boolean equals(Object obj) {
return (this == obj);
}
可以看到,默认的情况下equals就是比较两个的==操作。那么==操作是比较什么呢?
- 对于基本类型(int,byte,boolean,long,short,double,float,char),==操作比较值相等
- 对于对象类型,比较两个对象的内存地址。
- 对于封装类型和基本类型间的比较,编译器会转换为基本类型后再比较
下面这个对于某些人来说可能是个世界难题,
@Test
public void testEqualsInt(){
int a1=1;
Integer a2=new Integer("1");
Integer a3=new Integer("1");
Integer a4=1;
Integer a5=1;
Integer a6=999;
Integer a7=999;
//比较基本类型和封装类型,会转换为基本类型后比较
Assert.assertTrue(a1==a2);
//比较基本类型和封装类型,会转换为基本类型后比较
Assert.assertTrue(a1==a3);
//比较基本类型和封装类型,会转换为基本类型后比较
Assert.assertTrue(a2==a1);
//比较对象地址,new出来的显然是不同的
Assert.assertFalse(a2==a3);
//比较对象地址,这个套路比较深,java里面会的-128~127里面的整数做缓存,返回相同的对象,超出范围的才new
Assert.assertTrue(a4==a5);
//比较对象地址,这个套路比较深,java里面会的-128~127里面的整数做缓存,返回相同的对象,超出范围的才new
Assert.assertFalse(a6==a7);
}
所以我们可以很简单的得出:
@RunWith(JUnit4.class)
public class EqualsTests {
@Test
public void testEquals(){
Student o1=new Student("111");
Student o2=new Student("111");
Assert.assertFalse(o1.equals(o2));
}
@AllArgsConstructor
public static class Student{
private String name;
}
}
我们尝试着重新equals方法
@AllArgsConstructor
public static class Student{
private String name;
@Override
public boolean equals(Object obj) {
//满足非空性
if(obj==null){
return false;
}
//自反性
if(this == obj){
return true;
}
if(!obj.getClass().equals(this.getClass())){
return false;
}
Student student=(Student)obj;
//比较名称
if(this.name==null){
return student.name==null;
}
return this.name.equals(student.name);
}
}
可以看到,我们做了很多额外的检查,来保证这两个相等性,我们的junit
@Test
public void testEquals(){
Student o1=new Student(null);
Student o2=new Student(null);
Student o3=new Student("1111");
Student o4=new Student("1111");
Student o5=new Student("2222");
Assert.assertTrue(o1.equals(o2));
Assert.assertFalse(o1.equals(o3));
Assert.assertFalse(o1.equals(null));
Assert.assertTrue(o3.equals(o4));
Assert.assertFalse(o4.equals(o5));
}
我们可以用EqualsBuilder来快速重写equals方法
public static class Student{
private String name;
@Override
public boolean equals(Object obj) {
if(obj instanceof Student){
return new EqualsBuilder().append(this.name,((Student) obj).name).isEquals();
}
return false;
}
}
更简单的,如果我们使用lombock,可以直接添加,
@AllArgsConstructor
@EqualsAndHashCode(of = "name")
public static class Student{
private String name;
}
注意,这边会同时重写hashCode,在下面的集合测试里我们不会使用这种方式,会影响Hash类型的集合的结果。
在java里面很多类都重写了equals,比如String类的
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
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;
}
equals在集合里的作用
@Test
public void testEqualsInArray(){
Student o1=new Student(null);
Student o2=new Student(null);
Student o3=new Student("1111");
Student o4=new Student("1111");
Student o5=new Student("2222");
ArrayList<Student> list=new ArrayList();
HashSet<Student> set=new HashSet();
list.add(o1);
set.add(o1);
Assert.assertTrue(list.contains(o1));
Assert.assertTrue(list.contains(o2));
Assert.assertTrue(set.contains(o1));
//没有重写hashCode导致HashSet无法正确判断元素
Assert.assertTrue(set.contains(o2));
}
可以看到,在使用HashSet的时候,尽管我们的equals是返回相同的对象,但是运行结果依旧是false。有关这部分的知识我们会留到HashCode的部分加以说明。