1、==和equeals的区别?
== 和 equals 的区别 :
-
==
是运算符,如果是基本数据类型,则比较的是其存储的值是否相等;如果是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。 -
equals的作用是判断两个对象是否相等,如果对象重写了equals()方法,比较两个对象的内容是否相等;如果没有重写,比较所指向对象的地址值是否相同 ,等价于“==”。
1、 equals是Object的方法,本质上相当于==
,比较的是引用类型的变量所指向的对象的地址值是否相等,不能用于比较基本数据类型,源码如下:
public boolean equals(Object obj) {
// 实际上返回的就是使用==进行比较的结果
return (this == obj);
}
2、所有的类都继承Object,equals是Object对象中的非final方法,目的就是被用来覆盖(override)的,所以当我们使用equal()方法进行比较的时候,需要关注的就是这个类有没有重写Object中的equals()方法,一般情况下,类都会重写equals方法用来比较两个对象的内容是否相等。比如String类,Integer类中的equals()是被重写了,比较的是对象的值。String类的equals()方法如下:
public boolean equals(Object anObject) {
// 先使用==比较两个String对象的地址值是否相同,如果地址值相同,直接返回true
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
// 比较两个String对象的内容是否相同
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;
}
3、自定义一个Person类,没有重写Object类的equals()方法 :
public class Person {
String name;
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("张三",18);
Person person2 = new Person("张三",18);
System.out.println(person1.equals(person2)); // false
}
}
4、自定义一个Person类,重写Object类的equals()方法 :
public class Person {
String name;
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
// 先使用==比较地址值是否相同,如果相同说明是同一个对象,直接返回true
if (this == o) {
return true;
}
if(o instanceof Person){
Person person = (Person) o;
return name.equals(person.name) && age==person.age;
}
return false;
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("张三",18);
Person person2 = new Person("张三",18);
System.out.println(person1.equals(person2)); // true
}
}
2、hashcode()和equals()方法的关系?
两个对象的hashcode相同,则equals一定为true对吗?
你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?equals和hashCode都是Object对象中的非final方法,它们设计的目的就是被用来覆盖(override)的,所以在程序设计中还是经常需要处理这两个方法。
(1) hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
(2) equals它的作用也是判断两个对象是否相等,如果对象重写了equals()方法,比较两个对象的内容是否相等;如果没有重写,比较两个对象的地址是否相同,价于“==”。
一般一个类的对象如果会存储在HashTable,HashSet,HashMap等散列存储结构中,那么重写equals后最好也重写hashCode,否则会导致存储数据的不唯一性(存储了两个equals相等的数据)。而如果确定不会存储在这些散列结构中,则可以不重写hashCode。但是个人觉得还是重写比较好一点,谁能保证后期不会存储在这些结构中呢,况且重写了hashCode也不会降低性能。
1、一个类的对象如果不会存储在HashTable,HashSet,HashMap等散列存储结构中
在这种情况下, 该类的hashCode()
和 equals()
没有半毛钱关系的!。equals() 用来比较该类的两个对象是否相等,而hashCode() 则根本没有任何作用,所以,不用理会hashCode(),但就像前面说的,最好也重写hashcode()方法,万一以后用到了呢?
public class Person {
String name;
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
// 先使用==比较地址值是否相同,如果相同说明是同一个对象,直接返回true
if (this == o) {
return true;
}
if(o instanceof Person){
Person person = (Person) o;
return name.equals(person.name) && age==person.age;
}
return false;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
System.out.println(p1.equals(p2)); // true
System.out.println("p1:"+ p1.hashCode() +" p2:"+p2.hashCode()); // p1:68545 p2:68545
}
}
从结果也可以看出,p1和p2相等的情况下,hashCode()也不一定相等。
2、一个类的对象如果会存储在HashTable,HashSet,HashMap等散列存储结构中
在这种情况下,该类的“hashCode() 和 equals() ”是有关系的:
(1) 若两个对象equals返回true,则hashCode必须相等,否则会存储重复的元素
(2) 若两个对象hashCode相等,则equals不一定返回true,因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等,此时就出现所谓的哈希冲突场景。
public class Person {
String name;
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
// 先使用==比较地址值是否相同,如果相同说明是同一个对象,直接返回true
if (this == o) {
return true;
}
if(o instanceof Person){
Person person = (Person) o;
return name.equals(person.name) && age==person.age;
}
return false;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
HashSet<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);
System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode()); // 685325104
System.out.println(p2.hashCode()); // 460141958
System.out.println(set);// [Person{name='eee', age=100}, Person{name='eee', age=100}]
}
}
HashSet用于存放不重复的元素,我们在Person类中重写了equals()方法,但是发现:HashSet中仍然有重复元素:p1 和 p2,为什么会出现这种情况呢?因为p1和p2的内容虽然相等,但是它们的hashCode()不等,所以HashSet在添加p1和p2的时候,认为它们不相等,它们被添加到了HashSet的两个不同桶中。
在Person类中重写hashcode()方法:
public class Person {
String name;
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
// 先使用==比较地址值是否相同,如果相同说明是同一个对象,直接返回true
if (this == o) {
return true;
}
if(o instanceof Person){
Person person = (Person) o;
return name.equals(person.name) && age==person.age;
}
return false;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int hashCode() {
int nameHash = name.toUpperCase().hashCode();
return nameHash ^ age;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
Person p3 = new Person("EEE", 100);
HashSet<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(p1.equals(p2)); // true
System.out.println("p1:"+p1.hashCode() + ", p2:"+ p2.hashCode()); // p1:68545, p2:68545
System.out.println(p1.equals(p3)); // false
System.out.println("p3:"+p1.hashCode() + ", p4:"+ p3.hashCode()); // p3:68545, p4:68545
System.out.println(set); // [Person{name='eee', age=100}, Person{name='EEE', age=100}]
}
}
p1和p2的hashCode()相等,equals()方法也返回true,则p1和p2相等。
p1和p3的hashCode()相等;但equals()方法返回false。则p1和p3不相等。
此时,HashSet中没有重复元素,所以,如果类使用再散列表的集合对象中,要判断两个对象是否相同,除了要覆盖equals()之外,也要覆盖hashCode()函数。否则,equals()无效,为什么吗?我们来看看HashSet的源码:
可以看出,hashSet使用的是hashMap的put方法,而hashMap的put方法中:首先计算添加元素key的hash值与桶中元素key的hash值是否相同,如果相同,再通过equals()比较key值是否相同,如果相同,返回同一个对象。
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
3、总结
如果一个类的对象如果不会存储在HashTable,HashSet,HashMap等散列存储结构中 : hashcode()方法和equals()方法没有关系,比较两个对象是否相等时,重写equals()方法即可。
如果一个类的对象如果不会存储在HashTable,HashSet,HashMap等散列存储结构中 : hashcode()方法和equals()方法就必须满足下面的关系 。
(1) equals() 和hashCode() 需要同时覆盖,重写equals()方法就必须重写hashcode()方法;
(2) 若两个对象的equals()方法返回true,则hashCode必须相等,否则会存储重复的元素,因为hashcode不同这两个对象会存储到HashSet不同的桶中;
(3) 若两个对象equals返回false,hashcode可能相等,即出现哈希冲突,不同的两个对象分到同一个桶中;
(4) 若两个对象hashCode相等,则equals不一定返回true,即出现哈希冲突,不同的两个对象分到同一个桶中;
(5) 若两个对象hashCode不等,则equals一定返回false,否则会导致存储重复元素 ;