前言
之前学习java核心技术的时候有一节是将hashCode和equals的,在我的印象里面,hashCode是返回对象的内存地址的,equals方法重写的时候,hashCode方法也要重写,认识的程度也仅限于此,前两天学习集合普遍用到hashCode,回来再好好学学,这个还是比较重要的
HashCode
为什么要用hashCode?查一下官方文档
hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。
hashCode 的常规协定是:
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)
当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码
我们可以总结以下几小点作用:
hashCode存在的意义是为了hash表查找的快速,经过之前的HashMap的源码我们可以看出,hashCode是计算index的必备,但是它也仅仅是计算出index,在发生碰撞的情况下,hashCode相等没错,但是它们的对象不一定是同一个啊!所以说,事实上,hashCode并不是指向真正的地址!有没有?只是index罢了
两个对象相同,equals会相同True,它们的hashcode也要相同,equals重写的时候,hashcode也要重写
hashCode相同,不代表对象相同!只是代表index相同,都在一个桶里面
举个简单的例子,说明hash查找法的强大
hashCode的宗旨还在于查找
在我们学习的初期,最基本的查找算法是什么?你可能会说二分法,一个一个的找对吧,然后去和存储的内容去比对,这一个一个地找下来,对比下来那消耗肯定非常巨大了,如果用上hash,比如HashMap中的查找,我通过hash运算找到index,桶的位置然后再用equals进行对比查找。
个人感觉我见过的类里面好多都有HashCode这个方法啊- -它具体实现,这个就没必要看了
equals
从Object说起
1、Object.equals
public boolean equals(Object obj) {
return (this == obj);
}
Object 用的是==比的是内存地址
2、Objects.equals
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
Objects.equals比的是地址和两个对象equals返回的值
3、String.equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
我们通过查看String来看看实际中的equals重写,我们可以得出equals几步走
1、判断是否是同一引用(同一对象)
2、根据自己需要判断是instanceof 还是getClass
3、类型强制转换
4、制定自己的比较策略
String的比较 策略是,字节码的一一对比,先判断编码,然后编码的对比
@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
两个值的字节数组的一一对比。
不得不提的一点,getClass还是instanceof
至于使用instanceof 还是getClass,是要根据实际情况看的,当你实际中忽视子类和父类的区别的时候可以用instanceof,但是如果父类和子类是有区别的,比如员工和经理,员工和经理之间进行显然是不合适的,请看下面的例子
package compare;
import java.util.Objects;
public class Employee implements Comparable<Employee>
{
private String name;
private double salary;
public Employee(String name, double salary)
{
this.name = name;
this.salary = salary;
}
public String getName()
{
return this.name;
}
public double getSalary()
{
return this.salary;
}
@Override
public boolean equals(Object otherObject)
{
if (otherObject == null)
{
return false;
}
if (this == otherObject)
{
return true;
}
if (this.getClass() != otherObject.getClass())
{
return false;
}
Employee other= (Employee) otherObject;
return this.getSalary() == other.getSalary()
&& Objects.equals(getName(), getName());
}
@Override
public int hashCode()
{
return Objects.hash(this.name,this.salary);
}
@Override
public String toString()
{
return getClass().getName() + "[ name: "+this.getName() + ", salary: " + this.getSalary() + " ]";
}
}
这里的员工的比较对象必须是另外一个员工才行,同时hashcode也要重写,用name和salary作为参数,保证equals相等,hashcode也是相等的
总结
总的来说,hashCode在散列表中的查找起着重要的作用,而equals的重写在于自己制定策略来比较两个对象的相等性,只有真正看过源码,才能知道它们真正的作用,java的hashcode不是内存地址!!!!!- -记住哦