最近在找工作,回顾Java基础的同时,做一些总结和笔记。这一篇博客,主要从三个方面去展开:第一部分是.equals和"=="的区别;第二部分是自定义Object时重写equals()方法和hashCode()方法;第三部分是从哈希表介绍为什么要重写hashCode方法。希望这篇博客对碰巧看到的朋友有所帮助吧。
一..equals()和“==”的区别
.equals()和"=="的区别,相信有Java基础的朋友都知道。.equals方法比较的是两个对象的内容,而''=="比较的是两个对象的内存地址。
在我们创建一个对象的时候,会分配一块内存地址。例如,我们使用如下两种方式去创建一个String对象:
String s1 = "java";
String s2 = new String("java");
上面创建String对象的方式,区别在哪?第一种方式创建一个String对象,是非常特殊的一种情况。首先,在内存中找是否存在"java"这个对象,如果有,就让s1指向那个"java"。如果内存里没有"java",就创建一个新的对象"java"。第二种方式创建一个String对象,使用new,每次new一个对象,都会分配一块新的内存地址。也就是说,不管内存中是否存在"java"这个对象,都新创建一个对象。因此,我们如果比较s1和s2,其内容是一样的,其内存地址是不一样的。
我们看一下下面这段代码:
public class EqualsDemo {
public static void main(String[] args) {
String s1 = "java";
String s2 = new String("java");
String s3 = s1;
System.out.println("s1.equals(s2):" + (s1.equals(s2)));
System.out.println("s1 == s2:" + (s1 == s2));
System.out.println("s1 == s3:" + (s1 == s3));
}
}
最后的输出结果:
s1.equals(s2):true
s1 == s2:false
s1 == s3:true
(1)s1和s2的内容是一样的,因此第一种输出为true。
(2)s1和时的内存地址不一样,因此第二种输出为false。
(3)s3 = s1,因为内存中已经有“java”,因此不会创建新的对象,直接指向s1的地址,因此为true。
接下来,看一下下面一段代码:
public class EqualsDemo {
public static void main(String[] args) {
String s4 = "a";
String s5 = s4 + "b";
String s6 = "a" + "b";
System.out.println(s5 == "ab");
System.out.println(s6 == "ab");
}
}
输出结果为:
false
true
(1)s5是变量s4的值和字符串常量“b”拼接得到的变量,与“ab”的地址不一样,因此为false。
(2)s6 = "a"+"b",其实就是s6 = "ab",因此为true。
java编译器可以对字符串常量直接相加的表达式进行优化,在编译时去掉加号,直接将其编译成一个这些常量相连的结果。
二.自定义Object重写equals和hashCode方法
自定义Object的时候,我们往往会重写equals()方法和hashCode()方法。若不重写这两个方法,那么.equals比较的就是内存地址,相当于“==”。我们来看下面的例子,AObject和BObject是我自定义的两个Object,其中AObject没有重写两个方法,BObject重写了两个方法:
public class AObject {
private String name;
private String password;
public AObject(String name, String password) {
this.name = name;
this.password = password;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
public String toString() {
return super.toString();
}
}
public class BObject {
private String name;
private String password;
public BObject(String name, String password) {
this.name = name;
this.password = password;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + name.hashCode();
result = 31 * result + password.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (this.getClass() != obj.getClass()) {
return false;
}
BObject object = (BObject) obj;
return name.equals(object.name) && password.equals(object.password);
}
@Override
public String toString() {
return "name:" + name + ";" + "password:" + password;
}
}
接下来,我们使用下面的java代码测试一下AObject和BObject的equals方法:
public class EqualsDemo {
public static void main(String[] args) {
AObject aobj1 = new AObject("Jack", "111111");
AObject aobj2 = new AObject("Jack", "111111");
System.out.println(aobj1.equals(aobj2));
System.out.println(aobj1 == aobj2);
BObject bobj1 = new BObject("Jack", "111111");
BObject bobj2 = new BObject("Jack", "111111");
System.out.println(bobj1.equals(bobj2));
System.out.println(bobj1 == bobj2);
}
}
程序运行结果如下:
false
false
true
false
对于AObject,因为我们没有重写他的.equals方法,因此.equals方法比较的就是对象的内存地址,因此两个结果都是false。对于BObject,因为我们重写了他的.equals方法和hashCode方法,当BObject的name和password一样的时候,我们返回true,因此第一个结果为true。而第二个比较的是两个对象的内存地址,因此为false。
三.为什么要重写hashCode方法
为什么我们在自定义Object的时候要重写hashCode方法呢?不重写可以吗?其实,不重写也是可以的,只要我们不把我们自定义的Object作为HashTable、HashMap或者HashSet这样的hash集合的key即可。
首先,我们复习一下哈希表的相关知识。哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,哈希表不是按照equals来判断两个对象是否相等。给哈希表一个键值,他会用hashcode方法取得这个键值的哈希码也就是hashcode值,把它作为实际的索引来进行访问。
接下来,我们看一下,假如在BObject中我们没有重写hashCode方法,也就是:
public class BObject {
private String name;
private String password;
public BObject(String name, String password) {
this.name = name;
this.password = password;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (this.getClass() != obj.getClass()) {
return false;
}
BObject object = (BObject) obj;
return name.equals(object.name) && password.equals(object.password);
}
@Override
public String toString() {
return "name:" + name + ";" + "password:" + password;
}
}
我们使用如下的java代码进行测试:
import java.util.HashMap;
public class EqualsDemo {
public static void main(String[] args) {
BObject bobj1 = new BObject("Jack", "111111");
BObject bobj2 = new BObject("Jack", "111111");
HashMap<BObject, String> hashmap = new HashMap<BObject, String>();
hashmap.put(bobj1, "a");
hashmap.put(bobj2, "b");
System.out.println("bobj1.equals(bobj2):" + bobj1.equals(bobj2));
System.out.println("size:" + hashmap.size());
System.out.println("a:" + hashmap.get(bobj1));
System.out.println("b:" + hashmap.get(bobj2));
}
}
输出结果为:
bobj1.equals(bobj2):true
size:2
a:a
b:b
上面的结果是不是很诡异?为何两个对象的内容是相同的,但是,作为key被放进HashMap后,却被认为是两个不同的对象。这就是因为,我们重写了equals方法,因此两个对象相同是没问题的。但是,HashMap拿到的hashCode却是不一样的,因此,使用这两个对象作为key查询到的结果也是不一样的。
我们把BObject的hashCode方法恢复到重写的状态,再次运行一下,结果如下:
bobj1.equals(bobj2):true
size:1
a:b
b:b
在这里,对HashMap不熟悉的,可能还会有小小的疑问,为什么最后得到的结果都是b,而不是a?这是因为,使用同一个key去put值的时候,第二次是更新了原先插入的值,因此,得到的结果都是b。以上就是今天对.equals和“==”的区别和自定义Object要重写equals和hashCode方法的一些总结,如果有写的不对的地方,欢迎指正。