hashCode() 和equals() 完美CP
hashCode()
和equals()
在java对象的父类Object
中定义了方法,所以所有java对象都继承这些方法的默认实现。
以下是Object对象API关于equal方法和hashCode方法的说明:
public boolean equals(Object obj)
指示其他某个对象是否“等于”当前这一个这对象。
equals方法在非空对象引用上实现等价关系:
* 自反性(reflexive)。对于任意不为null的引用值x,x.equals(x)一定是true。
* 对称性(symmetric)。对于任意不为null的引用值x和y,当且仅当x.equals(y)是true时,y.equals(x)也是true。
* 传递性(transitive)。对于任意不为null的引用值x、y和z,如果x.equals(y)是true,同时y.equals(z)是true,那么x.equals(z)一定是true。
* 一致性(consistent)。对于任意不为null的引用值x和y,如果用于equals比较的对象信息没有被修改的话,多次调用时x.equals(y)要么一致地返回true要么一致地返回false。
* 对于任意不为null的引用值x,x.equals(null)返回false。
Object类的equals方法实现对象上最可能的等价关系 ;也就是说,对于任何非空引用值x和y,当且仅当x和y引用同一对象(x == y的值为true)时,此方法返回true。
请注意,无论何时重写此方法,通常都必须重写hashCode方法,以维护hashCode方法的一般约定,该方法声明相等对象必须具有相同的哈希代码。
public int hashCode()
返回对象的哈希码值。这种方法支持散列表(如HashMap提供的散列表)。
hashCode的一般约定是:
* 在执行Java应用程序期间,每当它在同一对象上被调用多次, hashCode方法必须始终返回相同的整数,前提是在修改对象的 equals 比较时不使用任何信息。 该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。.
* 如果两个对象根据equals(Object)方法相等 , 然后在这两个对象的每一个上调用hashCode方法必须产生相同的整数结果.
* 根据equals(java.lang.Object)方法,并不要求两个对象不相等, 然后在这两个对象的每一个上调用hashCode方法必须产生不同的整数结果. 但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高散列表的性能。
equals()
Object对象equals()
public boolean equals(Object obj) {
return (this == obj);
}
由上可知,当类对象没重写 equals()方法时,根据 == 运行算符判断是否同一对象实例(内存地址),如何重写它,视需求而定, 如String类重写equals()来判断字符串的值是否相等。但必须按照约定重写hashCode()
hashCode()
那为什么要重写hashCode
,因为它支持散列表啊!!
在Java中Hashtable和HashMap
是链表散列数据结构,而HashSet/LinkedHashMap
基于HashMap
储取数据, 哈希表的原理可自行了解
Hash集合
当向hash集合中添加某个元素,集合会先调用 hashCode()
,直接定位它所存储的位置,若该处没有其他元素,则直接保存。若该处已经有元素存在,就调用 equals()
来匹配这两个元素是否相同,相同则不存,不同则散列到其他位置。这样处理,当我们存入大量元素时就可以大大减少调用 equals()
方法的次数,极大地提高了效率。下面通过例子验证
public class ObjectEqualsTest {
public static void main(String[] args) {
Set<Person> personSet = new HashSet<>();
Person p1 = new Person(8, "武庚");
personSet.add(p1);
Person p2 = new Person(16, "武庚");
System.out.println("add p2 before");
personSet.add(p2);
System.out.println("add p2 after");
System.out.println(personSet);
Hashtable<Person, Object> hashtable = new Hashtable<>();
hashtable.put(p1, p1);
hashtable.put(p2, p2);
System.out.println(hashtable);
}
}
class Person {
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
/*IDE 生成code*/
@Override
public boolean equals(Object o) {
System.out.println("Person#equals");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
/*IDE 生成code*/
/*@Override
public int hashCode() {
int result = age;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}*/
/*模拟hash 冲突*/
@Override
public int hashCode() {
return name.hashCode();
}
}
通过在equals() 断点 debug 时发现在 HashMap#putVal
方法中调用了equals()
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
Hashtable#put
方法中调用了equals()
if ((entry.hash == hash) && entry.key.equals(key))
重写equals()和hashcode()小结
- 当
a.equals(b)
,那么a.hashCode()
必须与b.hashCode()
相同 - 用一个对象的相同属性来生成
hashCode()
和equals()
两者 - 使用集合时,尤其
HashMap
、Hashtable
,根据目标类重写equals()
和hashcode()