==

对于基本类型,== 比较的是字面量。

对于引用类型,== 比较的是地址值。

String a = new String("abc");
String b = new String("abc");
String aa = "abc";
String bb = "abc";
// false,因为用 new 关键字创建 String 对象,每次都是新对象,地址值都不同
System.out.println(a == b);
// true,因为用字符串字面量创建 String 对象,都指向位于 Srping pool 中的同一个对象,地址值相同
System.out.println(aa == bb);
// true,基本类型比较的是字面量
System.out.println(42 == 42.0);

equals()

分两种情况

1.没有重写 equals(),则比较的是地址值,相当于 == 比较。

public class Test03 {

	public static void main(String[] args) {
		User user1 = new User("张三",15);
		User user2 = new User("张三",15);
		// false,因为 User 类没有重写 equals(),实际还是通过 == 比较
		System.out.printf("user1.equals(user2): %s \n",user1.equals(user2));
	}
	
	public static class User{
		String name;
		int age;
		public User(String name, int age) {
			this.name = name;
			this.age = age;
		}
	}
}

看一下 Object 中的 equals()

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

可以看到用的是 == 比较

2.重写了 equals()

我们可以在 equals() 里面先比较两个对象是否相等,如果不相等,再去比较内容是否相等。

public class Test03 {

	public static void main(String[] args) {
		User user1 = new User("张三",15);
		User user2 = new User("张三",15);
		// true,因为重写了 equals()
		System.out.printf("user1.equals(user2): %s \n",user1.equals(user2));
	}
	
	public static class User{
		String name;
		int age;
		public User(String name, int age) {
			this.name = name;
			this.age = age;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			User other = (User) obj;
			if (age != other.age)
				return false;
			if (name == null) {
				if (other.name != null)
					return false;
			} else if (!name.equals(other.name))
				return false;
			return true;
		}
	}
}

String 类也重写了 equals(),所以我们可以直接拿来比较。

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;
}

hashCode()

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

通俗的将,在 Java 中,你要在本质是散列表的集合中用到该对象,如:HashMap,Hashtable,HashSet,那么 hashCode() 才起作用,其他情况下没用。

1.一般情况,只重写 equals(),没有重写 hashCode()

public class Test03 {

	public static void main(String[] args) {
		User user1 = new User("张三",15);
		User user2 = new User("张三",15);
		System.out.printf("user1.equals(user2): %s, user1 hashCode: %d, user2 hashCode: %d \n",user1.equals(user2),user1.hashCode(),user2.hashCode());
		
	}
	
	public static class User{
		
		String name;
		
		int age;
		
		public User(String name, int age) {
			this.name = name;
			this.age = age;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			User other = (User) obj;
			if (age != other.age)
				return false;
			if (name == null) {
				if (other.name != null)
					return false;
			} else if (!name.equals(other.name))
				return false;
			return true;
		}
		
	}
}

输出:

user1.equals(user2): true, user1 hashCode: 382503496, user2 hashCode: 721139189 

可以看到 hashCode 不相等,并不影响 equals() 比较。

2.往 HashSet 中添加该对象

User user1 = new User("张三",15);
User user2 = new User("张三",15);
System.out.printf("user1.equals(user2): %s, user1 hashCode: %d, user2 hashCode: %d \n",user1.equals(user2),user1.hashCode(),user2.hashCode());
HashSet<User> set = new HashSet<>();
set.add(user1);
set.add(user2);
System.out.println(set);

输入:

user1.equals(user2): true, user1 hashCode: 1936129502, user2 hashCode: 365113514 
[User [name=张三, age=15], User [name=张三, age=15]]

可以看到这两个对象的内容是重复的,因为往 HashSet 中添加该对象时,会计算对象的 hashCode 值来判断对象加入的位置是否相同,如果不相同,那么就认为这两个对象不相等。

所以虽然 user1 和 user2 的内容相等,但是它们的 hashCode 不相等。所以 HashSet 在添加它们时,会认为它们不相等。

3.重写 HashCode()

@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + age;
	result = prime * result + ((name == null) ? 0 : name.hashCode());
	return result;
}

输出:

user1.equals(user2): true, user1 hashCode: 776315, user2 hashCode: 776315 
[User [name=张三, age=15]]

可以看到 user1 和 user2 的 hashCode 相等,往 HashSet 中添加它们时,首先会根据 hashCode 判断对象加入的位置是否相同,如果相同,那么就再去判断它们的内容是否一样,如果一样,那么会认为它们是相等的。

总结

1.对于基本类型,== 比较的是字面量

2.对于引用类型,== 比较的是地址值

3.没有重写 equals(),则比较的是地址值,相当于 == 比较

4.重写 equals(),则比较内容是否相等

5.对象相等,hashCode() 一定相等

6.hashCode() 相等,对象不一定相等

7.对象相等,equals() 一定相等

8.equals() 相等,对象不一定相等

9.equals() 和 hashCode() 没有关系

10.在本质是散列表的集合中使用某个对象,则必须重写该对象的 equals() 和 hashCode() 方法

原文链接:https://miansen.wang/2019/09/11/java-==-equals-hashCode/