在 java.lang.Object 类中有两个非常重要的方法:
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
Object 类是类继承结构的基础,是每一个类的父类,都实现了Object 类中定义的方法。
equals()方法:
equals()方法是用作对象之间进行比较的,判断是否相等。
在 Object 类中 equals() 方法是 == 对两个对象之间的地址值进行的比较(引用是否相同)。
String 封装类中在使用 equals() 方法时,已经重写了父类 Object 的 equals() 方法。
public boolean equals(Object anObject) {
// 查看地址值是否相同,是否为同一引用
if (this == anObject) {
return true;
}
// 是否是String 类型
if (anObject instanceof String) {
// 强转为 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() 方法拥有的特性:
x 和 y 都不为 null
- 自反性:x.equals(x) 一定为 true
- 一致性:如果 x,y 的信息没有被修改过,x.equals(y) 被多次调用也会返回相同的值
- 对称性:x.equals(y) = true 则 y.equals(x) = true
- 传递性:x.equals(y) = true ,y.equals(z) = true 则 x.equals(z) 一定为 true
- 对于 x.equals(null) 一定返回 false
当 equals() 方法被重写的时候,hashCode() 方法一定要重写,因为两个相同对象的 hashCode 一定相同。
hashCode()方法:
hashCode() 方法给对象返回一个 hashCode 值。这个方法被用于 hash tables,例如 HashMap
hashCode() 方法拥有的性质:
- 在一个Java程序运行期间,如果一个对象提供给 equals() 作比较的信息没有变化的话,该对象多次调用 hashCode() 方法,返回的应该是同一个Integer。
- 如果两个对象的 equals() 结果是 true。则两个对象的 hashCode 值一定相同。
- 并不是两个不相同的对象返回的 hashCode 值一定不同。
在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;
}
在Java中有两种集合(List 和 Set),List 有序可重复的,Set 无序不可重复的。
Set中要想保证数据不重复,根据什么来判断?
如果每增加一个元素都要进行 equals() 比较一次,当添加元素数据足够多(x)的时候,新增一个元素需要比较的次数就会为x次,需要调用x次 equals() 方法,这样性能就会特别低。于是Java采用了哈希表的原理(Hash算法,也称之为散列算法),是将数据依照特定的算法指定到一个地址上。这样每次新增数据的时候只需要调用 hashCode() 方法就能定位到存储的位置,如果这个位置上没有元素就可以直接存储,如果这个位置上有值则只需要调用一次 equals() 方法比较两个元素是否相同,相同的话覆盖,不相同的话重新散列到其他位置。这样就大大降低了 equals() 的比较次数了
简而言之:相同的对象,他们的HashCode 一定相同。两个对象的 HashCode 值相同,他们不一定相等
示例:
// 创建一个Person类,实现 getter/setter方法,以及toString 方法
@AllArgsConstructor
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
// 测试类
public class TestDemo {
public static void main(String[] args) {
HashSet<Person> hashSet = new HashSet<>();
hashSet.add(new Person("zhangsan",18));
hashSet.add(new Person("zhangsan",18));
hashSet.add(new Person("zhangsan",18));
System.out.println("hashSet = " + hashSet);
}
}
// 输出结果
hashSet = [Person{name='zhangsan', age=18}, Person{name='zhangsan', age=18}, Person{name='zhangsan', age=18}
当 Person 类没有重写 equals 和 hashCode 方法时,加入到HashSet中,每个对象生成的hashCode值都不相同,所以没办法判断重复
@AllArgsConstructor
public class Person {
private String name;
private int age;
// getter/setter,toString方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
// 调用测试类
public class TestDemo {
public static void main(String[] args) {
HashSet<Person> hashSet = new HashSet<>();
hashSet.add(new Person("zhangsan",18));
hashSet.add(new Person("zhangsan",18));
hashSet.add(new Person("zhangsan",18));
System.out.println("hashSet = " + hashSet);
}
}
// 输出结果
hashSet = [Person{name='zhangsan', age=18}]