总览equals( ) 方法
-
理解equals()方法和==运算符的区别是非常重要的。
- 默认情况下(即没有被重写时)equals()只能比较引用类型,"=="既能 比较引用类型又能比较基本类型。
- equals()方法从Object类继承,即比较对象引用的值
- 一般都被子类方法覆盖,不再比较引用的值
-
"=="运算符:
- 比较基本数据类型:相当于算术等号。
- 比较引用数据类型:比较引用的值,不能被覆盖。
-
通常情况,子类要重写equals( ),改变它的含义。所以有的类中 equals( )是比较地址,有的类中该方法就不比较地址,具体的,就看子类新定义的该方法的规定。建议看看包装类中的equals()方法,蛮有趣的。
-
在Java中有个规定:如果equals( )返回两个对象是相等的,那这两个对象上调用hashCode( )返回的整数必须相等。否则在使用Hash类型集合时就会产生错误。
-
注意:覆盖equals( )方法同时,还要记得覆盖hashCode( )方法。 需要说明,如果equals( )返回两个对象不等,它们的hashCode( )也 可以返回相同的整数。但是最好让它们的hashCode( )返回不同的整 数,这有利于提高Hash类型集合的性能。
-
重写equals方法时,一定要重写hashcode()方法吗?
-
hashcode的调用的条件:
- 想往map里面放一个类作为map的键值,这个类又是自己设计的;
- 虽然类不是自己写的,但是你修改了这个类的equals方法;
-
如果满足上述调用条件,就要注意重写hashcode方法。
这样 当你往map 里放值得时候,系统会调用这个对象的.hashcode()方法来生成相应的 hash值,来映射相应的对象。
-
-
如果同一个类的两个对象的属性值相等,那么他们的hashcode一定相等吗?这个要看你具体如何实现你的hashcode,如果你希望他们的值一样hashcode也一样,你就可以这样实现。 但是hashcode的实现,一般要满足几个特征,比如自反性、对称性、传递性那些。
等价关系与equals( )
离散数学的知识:
设R是A上关系,若R是自反的、对称的和传递的,则称R是A中的等价关系。
。
equals() 其实就是一种等价关系,它指定了某种方法(默认的hashCode()、重写的hashCode()、自定义的equals()判据等)来判定这种等价关系。
我们自己想要重写equals(),为了保证这种等价关系,就需要维护自反性、对称性、传递性。
其实,简单的测试还不太够,最好是有形式化的证明。
这篇文章中就涉及了equals()的自反性、对称性、传递性,做了简单的测试……其实我也很无奈,这是我在准备大学里一门核心专业课考试的前一晚总结的,内心真正比较喜欢的文章,but没人看,Readers更喜欢比较浮夸的文章
IDE自动生成equals( )和hashCode( )
如果你确实懒得重写equals()和hashCode(),IDE支持自动生成这两个方法。
首先,Person类已经完成到了这个程度:
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private Integer age;
public Person(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
}
}
Eclipse
import java.io.Serializable;
import java.util.Objects;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private Integer age;
public Person(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return Objects.equals(id, other.id) && Objects.equals(name, other.name);
}
}
IntelliJ IDEA
import java.io.Serializable;
import java.util.Objects;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private Integer age;
public Person(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(id, person.id) &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
String的hashCode( )
String有hash这个实例变量,它的定义如下:
private int hash; // Default to 0
它缓存了hashCode()方法的值,也就是说,第一次调用hashCode()的时候,会把结果保存在hash这个变量中,以后再调用就直接返回保存的值。
我们来看下String类的hashCode方法,代码如下:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
如果缓存的hash不为0,就直接返回了,否则根据字符数组中的内容计算hash,计算方法是:
s表示字符串,s[0]表示第一个字符,n表示字符串长度,表示31的n-1次方再乘以第一个字符的值。
为什么要用这个计算方法呢?这个式子中,hash值与每个字符的值有关,每个位置乘以不同的值,hash值与每个字符的位置也有关。使用31大概是因为两个原因,一方面可以产生更分散的散列,即不同字符串hash值也一般不同,另一方面计算效率比较高,31* h 与 32 * h - h 即 (h<<5)-h 等价,可以用更高效率的移位和减法操作代替乘法操作。
在Java中,普遍采用以上思路来实现hashCode,也就是所谓的"33-Times"算法,多看源码,其实Java很多地方都是这样的思路。
来源:CSDN
作者:进阶的JFarmer
链接:https://blog.csdn.net/weixin_43896318/article/details/104711163