equals与==运用和区别(深入源码理解)
文章目录
双等号(==)
双等号比较的两种情况
对于==号只需要考虑两种情况:
- 基本数据类型使用==,比较的是值
- 引用数据类型使用==,比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
源码和例子解析
Integer和Integer,Integer和int之间的比较
Integer a1=new Integer(4321);
Integer a2=new Integer(4321);
int a3=4321;
System.out.println("a1==a2:"+(a1==a2));//结果为false,因为a1,a2是两个不同的对象,其堆中的地址值自然不同
System.out.println("a1==a3:"+(a1==a3));//结果为true,因为Java的自动拆箱机制,比较的是两个数值
System.out.println("a3==a1:"+(a3==a1));//结果为false,原因同上
Integer a4=4321;
Integer a5=4321;
/**
* Integer a4=4321;
* Integer a5=4321;
* 相当于执行了
* Integer a4=Integer.valueOf (4321) ;
* Integer a5=Integer.valueOf (4321) ;
*/
System.out.println("a4==a5:"+(a5==a4));//结果为false,相当于创建了两个对象,其地址自然不同
Integer a6=100;
Integer a7=100;
/**
* 为什么这个又不一样呢
* 要根据装箱操作的源码
* public static Integer valueOf(int i) {
* if (i >= -128 && i <= 127)
* return IntegerCache.cache[i + 127];
* //如果i的值大于-128小于127则返回一个缓冲区中已存在的一个Integer对象
* return new Integer(i);
* //否则返回 new 一个Integer 对象
* }
* 可以看出如果是-128-127之间,装箱装的是同一个对象(为了节约内存)
*所以如果是-128到127之间的装箱操作,装的都是同样的对象!
*/
System.out.println("a6==a7:"+(a6==a7));//为true
Integer a8=128;
Integer a9=128;
System.out.println("a8==a9:"+(a8==a9));//为false
Integer a10=-128;
Integer a11=-128;
System.out.println("a10==a11:"+(a10==a11));//为true
a10=127;
a11=127;
System.out.println("a10==a11:"+(a10==a11));//为true
/**
* 注意,这种是直接生成对象的!
* 没有进行装箱操作得到地址值,而是直接在堆创建生成的地址!
* 所有不同的
*/
Integer aa1=new Integer(1);
Integer aa2=new Integer(1);
System.out.println("aa1==aa2:"+(aa1==aa2));//为false
Java自动装箱拆箱机制的类型
String中==的解析
String s1="12345";
String s2="12345";
String s3=new String("12345");
String s4=new String("12345");
/**
* String s1=="xxx"这种字符串创建方式,并不是在堆中创建
* 为了节省空间,是在运行时常量池,也就是在方法区中创建
* s1和s2指向的是同一个地址
*/
System.out.println("s1==s2:"+(s1==s2));//true
System.out.println("s1==s3:"+(s1==s3));//false,s1指向的是常量池的地址,s3指向的堆中的地址
System.out.println("s3==s4:"+(s3==s4));//false,s3和s4指向的是堆中不同的地址,所以false
equals
用法概述
equals没有被重写就是比较地址,相当于===
原因:
equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。
equals源码
显然java源码对String类进行了特殊处理,String的equals以及不是比较地址值了,而是比较字符串的内容了。
- 首先两个对象在堆里面,地址值就不可能相等,所以要比较两个对象内容而不是地址时,必然要重写equals方法,比较内容
- hashCode默认情况下表示是对象的存储地址,如果两个东西相同,那么作为HashMap的key时应该是一样的,所以既然相等的规则变化了,自然要重写hashcode方法,跟随变化!
HashSet的equals
HashSet本身就重写了equals!
HashSet<Integer> set1=new HashSet();
HashSet<Integer> set2=new HashSet();
set1.add(1);
set1.add(2);
set2.add(1);
set2.add(2);
System.out.println(set1.equals(set2));//返回true!
而且比较有意义的是
HashSet底层实现是通过HashMap实现的
数组的equals
Array的equals用的是原生的
Integer[] array1={1,2,3};
Integer[] array2={1,2,3};
System.out.println(array1.equals(array2));//false
//java.util.Arrays为我们提供了hashCode和euqals
System.out.println(Arrays.equals(array1,array2));//true
但是java.util.Arrays为我们提供了hashCode和euqals,比较的是数组中的内部元素。
ps启发:
我们也可以学习这种方式
可以在不重写的方法下写一个通用类其中的方法进行计算hashcode和equals!
JAVA中重写equals()方法为什么要重写hashcode()方法
为什么重载hashCode方法?
答案:一般的地方不需要重载hashCode,只有当类需要放在HashTable、HashMap、HashSet等等hash结构的集合时才会 重载hashCode,那么为什么要重载hashCode呢?就HashMap来说,好比HashMap就是一个大内存块,里面有很多小内存块,小内存块 里面是一系列的对象,可以利用hashCode来查找小内存块hashCode%size(小内存块数量),所以当equal相等时,hashCode必 须相等,而且如果是object对象,必须重载hashCode和equal方法。
例如我们有对象A和对象B,现在重写了equals方法,让两者相等了,那我们用散列表HashSet存储了对象A,然后用set.contains(B),是查不到B存在的!
对象重写equlas和hashcode实例
package com.company;
public class Main {
public static void main(String[] args) {
// write your code here
Person p1=new Person("l1",11,"男");
Person p2=new Person("l1",11,"男");
Person p3=new Person("l1",12,"男");
System.out.println(p1==p2);//false
System.out.println(p1.equals(p2));//true
System.out.println(p1.equals(p3));//false
}
}
class Person{
String name;
Integer age;
String sex;
@Override
public int hashCode() {
return 31*(this.name.hashCode()+31*this.age.hashCode()+31*(this.sex.hashCode()));
}
@Override
public String toString() {
return this.name+" "+this.age+" "+this.sex;
}
@Override
public boolean equals(Object obj) {
Person person1=(Person) obj;
return this.hashCode()==person1.hashCode();
}
public Person() {
}
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
outPut
false
true
false
Process finished with exit code 0
Ps: 如果要用equal方法,请用object<不可能为空>.equal(object<可能为空>))"target".equals(foo)