Earth Guardian

You are not LATE!You are not EARLY!

0%

Java hashCode 和 eqauls

Object 的默认定义

也就是 Java 中所有的对象都会包含这两个方法:

1
2
3
4
public boolean equals(Object obj) {
return (this == obj);
}
public int hashCode() {...}

HashCode

哈希码是一种数据结构的算法,从对象计算出来的一个能代表该对象的整数值,这样可以根据哈希码对两个对象做快速比对。
通俗来讲,一个对象的哈希码允许算法和数据结构将对象放入一个隔间,这个隔间可以关联相同的对象,也可以关联多个不同对象,相当于将一堆数据做了一个大致的分类,根据哈希码可以快速缩小范围。

0009-java-hashcode-equals-hashCode-object-samples.jpg

需要遵循的规则:

  • 在一个运行的进程中,相等的对象必须要有相同的哈希码
  • 相同的哈希码,对象不一定相等
  • 重写 equals,必须重写 hashcode
  • 不要把哈希码误用为 Key

两个对象比较时,我们可以先比对哈希码,然后再使用 equals 来比对,提高比对效率。

hashcode() 重写,可以根据类中各个对象实例的哈希码加权:

1
2
3
4
@Override
public int hashCode() {
return myObject1.hashCode() + myObject2.hashCode();
}

Object 类中,hashCode 方法是通过 Object 对象的地址计算出来的,所以同一个对象的哈希码总是相同的。

equals== 的区别

数据类型的存储:栈和堆

  • 基本数据类型只涉及一个存储区:是存在栈内存中的,保存的是数据值本身。

栈内存用于存放基本类型的变量和引用变量,当超过变量的作用域后,java 会自动释放掉为该变量分配的栈内存空间。

  • 引用数据类型涉及到两块存储区:对象本身是存储在堆内存中;引用变量是存储在栈内存中,并存放指向该对象堆内存的首地址。

堆内存用于存放由 new 创建的对象和数组,堆中分配的内存由 java 虚拟机自动垃圾回收器来管理。数组和对象在没有引用变量指向它的时候才变成垃圾,不能再被使用,但是仍然占着内存。在随后的一个不确定的时间被垃圾回收器释放掉,这个也是 java 比较占内存的主要原因

如下例所示:引用变量 a 在栈中存储的是,对象 String("AA") 在堆内存中的首地址。

1
2
3
4
String a = new String("AA");
String b = new String("AA");
System.out.println(a==b);
System.out.println(a.equals(b));

操作符 ==

== 操作符比较的是变量栈内存中存储的值。比如基本数据类型变量比较的是存储的值;引用数据类型的引用变量比较的是存储的首地址。

操作符 equals

equals 操作符的作用和目标是:比较引用变量指向的对象在堆内存中的值,所有对象都需要通过该操作符来比对是否相等。

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

但是,回过头来看 Object 中默认的 eqauls 方法中直接使用了 == 操作符,即需要两个引用变量必须指向同一个对象时才认为是相等的。所以通常自定义类中 eqauls 操作符需要重写,才能确保类对象比较的是堆内存中的值。

区别

综上,可以看到上例两个对象 String("AA")String("AA") 申请了两块堆内存,存储的值都是 AA ;但两块存储区首地址不一样,也就是两个引用变量 ab 在栈内存中存储的值不同。
所以, a==b 的比对结果是 false ,但 a.equals(b) 的比对结果是 true

String 的特殊性

查看 String 源码,可以理解它为不可改变的常量,平时使用的字符串拼接或者重新赋值,内部实现其实是重新申请一块存储区存放新字符串,并将首地址返回给引用对象。如下示例:

1
2
3
4
String c = "StringValue";
String d = "StringValue";
System.out.println(c == d);
System.out.println(c.equals(d);

上例中 StringValue 在编译时就分配了一个常量区存放该对象的值,引用变量 c 存放了该存储区的首地址;在下次出现相同的常量时 StringValue ,会共享同一个存储区,而不会重新申请空间,所以应用变量 d 存放了同一个首地址。即两次输出都为 true

参考文档