==:对于基本类型变量,使用==比较的是两个变量的值是否相等。对于引用类型变量,使用==比较的是两个变量的引用是否相等,即比较两个引用(对象)在内存中的地址值是否相等。由于任意两个对象在内存中的地址值都不一样,所以通过==比较两个对象的值肯定不相等。
其实==都是对数值进行比较,对于基本数据类型,比较两个变量的值,对于引用数据类型,比较两个对象在堆内存中的地址值(用16进制表示的数字),所以,==属于数值比较。
equals()方法:equals方法的作用也是判断两个对象在内存中的地址值是否相等。equals方法是Object类中的方法,其源码如下所示:
public boolean equals(Object obj) {return (this == obj);
}
由equals方法的源码可知,Object类中equals方法的作用与==的作用是一样的,都是比较两个对象在内存中的地址值是否相等,即判断两个对象是不是同一个对象。为了比较两个对象的内容是否相同,可以重写equals方法,如String、Integer、Date类中都对equals方法进行了重写。重写equals()方法之后,就是比较两个对象的内容是否相等,若它们的内容相等,则返回true(即认为这两个对象相等),否则,返回false。
对于“请解释字符串比较之中“==”和equals()的区别”这个问题,需要考虑到字符串已经将equals方法重写了。可以如下回答:==是比较两个字符串在堆内存中的地址值是否相等,equals方法是比较两个字符串的内容是否相等。
hashCode()方法:hashCode方法的作用是获取哈希码(也称为散列码,是一个int类型的整数),这个哈希码的作用是确定该对象在哈希表(散列表)中的索引位置。也就是说,hashCode()方法只有在散列表中才能发挥出其作用,在其它情况下几乎没什么作用。hashCode方法是Object类中的方法,返回的是对象在内存中的地址值(16进制表示的int类型的整数,占4个字节,32位二进制位)。由于任意两个对象在内存中的地址值都不一样,所以通过hashCode方法得到的int型整数也不一样。为了比较两个对象的内容是否相同,可以重写hashCode方法,如String类中重写的hashCode方法的源码如下所示:
public int hashCode() {
int h = hash; //Default to 0 String类中的私有变量
//private final char value[]; String类中保存字符串内容的数组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;
}
String源码中使用private final char value[];保存字符串内容,因此String是不可变的。
由于hashCode()方法只有在散列表中才能发挥出其作用,所以看一下hashCode方法在散列表中的作用。在Java集合中底层使用散列表实现的类有HashMap,Hashtable,HashSet等。由于Set集合中不允许存放重复的元素,所以HashSet中也不允许存放重复的元素,如何保证HashSet中不保存重复的元素呢?就是通过hashCode和equals方法来实现的。在HashSet中,基本的操作都是有HashMap实现的,因为HashSet底层是用HashMap存储数据的。假设将某个元素存放到HashSet集合中,首先根据hashCode方法计算出该元素在HashSet集合中的索引位置(元素的哈希值与HashMap集合大小取模),判断该位置是否已经存在元素,如果该位置为空,就将元素添加进去,如果不为空,再用equals方法比较该元素与已存在元素是否相等,如果相等,则不添加,否则新建一个结点添加该元素。
此处对HashMap的知识点做一个简单的介绍:在jdk1.7及以前版本,HashMap底层是用数组和链表的形式实现的,数组中存放的是桶,桶中存放的是链表的头结点,链表是用来解决哈希冲突的。如果多个元素的哈希值相等,但通过equals方法得到的值不等,就会产生哈希冲突。将具有哈希冲突的多个元素存放在同一个桶中的链表中,新添加的元素作为链表的头结点,原来的头结点作为新头结点的下一个节点。
有的面试官会问到“equals方法与hashCode方法有什么区别”,由上面分析可知,equals方法默认是比较两个对象在内存中的地址值是否相等,即两个对象是不是同一个对象,而hashCode方法默认是获取对象在内存中的地址值,根本不是对两个对象进行比较,因此,这两个方法的功能都不一样,无可比性。不过,equals方法与hashCode方法有一定的关系。
在重写equals方法与hashCode方法时,需要满足如下约定:
1. 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。
2. 如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。
3. 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。
即,如果两个对象根据equals方法判断相等,则根据hashCode方法得到的结果必须相同,如果根据hashCode方法得到的结果相同,则两个对象根据equals方法判断不一定相等。
在重写hashCode方法时,最好使用Objects的hash()方法,如Objects.hash(field1,field2)。
Objects的hash()和hashCode()比较,Objects的hash()方法源码如下所示:
public static int hash(Object... values) {return Arrays.hashCode(values);
}
这里调用Arrays类的hashCode()函数:
public static int hashCode(Object a[]) {if (a == null)return 0;int result = 1;for (Object element : a)result = 31 * result + (element == null ? 0 : element.hashCode());return result;
}
由此可知,Objects的hash()方法的作用是将接收到的所有属性的哈希值进行组合。