1 ==、equals()和hashCode()
1.1 ==
==操作符用来比较两个基本类型变量时(byte、short、char、int、long、float、double、boolean),比较的是值;用来比较引用类型变量时(非基本类型),比较的是引用的地址。
1.2 equals()
equals方法是基类Object的方法,用于比较两个对象的内容是否相同,Object类中equals方法的默认实现使用的==操作符比较。
所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals()方法,这个方法的初始行为是比较对象的内存地址,实际开发过程中重写了equals方法,重新定义比较规则,如:String、Integer、Date在这些类重写了equals()方法,而不再是比较类在堆内存中的存放地址了。
1.3 hashCode()
hashCode()返回的就是对象的存储地址,事实上这种看法是不全面的,确实有些JVM在实现时是直接返回对象的存储地址,但是大多时候并不是这样,只能说可能存储地址有一定关联。hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable(注意:集合中不允许重复的元素存在)。
当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?
也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了.
说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。下面这段代码是java.util.HashMap的中put方法的具体实现:
// 将“key-value”添加到HashMap中
public V put(K key, V value) {
// 若“key为null”,则将该键值对添加到table[0]中。
if (key == null)
return putForNullKey(value);
// 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
// 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//若“该key”对应的键值对不存在,则将“key-value”添加到table中
modCount++;
//将key-value添加到table[i]处
addEntry(hash, key, value, i);
return null;
}
put方法是用来向HashMap中添加新的元素,从put方法的具体实现可知,会先调用hashCode方法得到该元素的hashCode值,然后查看table中是否存在该hashCode值,如果存在则调用equals方法重新确定是否存在该元素,如果存在,则更新value值,否则将新的元素添加到HashMap中。
从这里可以看出,hashCode方法的存在是为了减少equals方法的调用次数,从而提高程序效率。
2 ==和equals()比较
2.1 基本数据类型的值比较使用==
基本数据类型,也称原始数据类型。它们之间的比较,使用==,比较的是他们的值。 即内容相同,我们就认为是相等的。
public class Test {
public static void main(String[] args) {
int i=5;
int j=5;
if(i==j) {
System.out.println("i和j相等!");
} else {
System.out.println("不相等!");
}
}
}
i和j相等!
2.2 复合数据类型的比较使用equals()
当(String,Integer,Date) 用==进行比较的时候,比较的是他们在内存中的存放地址,所以除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。由以下代码解析:
String str1 = new String("hello");
String str2 = "hello";
String str3 = "h";
System.out.println("str1==str2: " + (str1==str2)); //false
System.out.println("str1.equals(str2): " + str1.equals(str2)); //true
System.out.println("str1.hashCode(): " + str1.hashCode());
System.out.println("str2.hashCode(): " + str2.hashCode());
System.out.println("str3.hashCode(): " + str3.hashCode());
输出结果:
2.3 对象类型比较使用equals()
(1)==号:对比对象实例的内存地址来判断是否是同一对象实例,也可以说是判断对象实例是否物理相等。
(2)equals():当对象实例没有重写Object的equals方法时,equals方法判断的是对象实例的内存地址是否是同一对象实例。
//Object类的源码如下
public boolean equals(Object obj) {
return (this == obj);
}
(3)例子
public class Test {
public static void main(String[] args) {
Student s = new Student("BlueSky");
Student s1 = new Student("BlueSky");
if (s == s1) {
System.out.println("s和是s1相等!");
} else {
System.out.println("s和是s1不相等!");
}
if (s.equals(s1)) {
System.out.println("s和是s1相等!");
} else {
System.out.println("s和是s1不相等!");
}
}
}
s和是s1不相等!
s和是s1不相等!
3 HashCode()与equals()方法的关系
可以直接根据hashcode值判断两个对象是否相等吗?
肯定是不可以的,因为不同的对象可能会生成相同的hashcode值,相等的两个对象的hashcode值是相等的。虽然不能根据hashcode值判断两个对象是否相等,但是可以直接根据hashcode值判断两个对象不等,如果两个对象的hashcode值不等,则必定是两个不同的对象。如果要判断两个对象是否真正相等,必须通过equals方法。关系如下:
- 如果equals方法得到的结果为true,则两个对象的hashcode值必定相等;
- 如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同;
- 如果两个对象的hashcode值不等,则equals方法得到的结果必定为false;
- 如果两个对象的hashcode值相等,则equals方法得到的结果未知。