结论:
1. 对于基本数据类型,==比较的是值,equals方法不能使用
2. 对于String和包装类,==比较的是地址,equals方法比较的是值
3. 对于继承Object的类,==比较的是地址,equals比较的也是地址
1.基本数据类型
==比较的是值
equals()是不能使用的(因为eauqls()是Object类的方法,对象才能调用)
例:
int a=100;
int b=100;
System.out.println("a=b"+(a==b));//true
System.out.println("a.equals(b)"+a.equals.(b));//编译报错
2.String和包装类
==比较的是内存中存放的地址
equals()比较的是值,因为它们重写了Object的equals()方法(类似还有Math,Date等值类型的类)
String部分
例如
String s1="hello";
String s2="hello";
String s3=new String("hello");
String s4=new String("hello");
String s5 = s3;
String s6 = new String(s3);
System.out.println("s1=s2"+(s1==s2));
System.out.println("s1=hello"+(s1=="hello"));
System.out.println("s3=s4"+(s3==s4));
System.out.println("s5=s3"+(s5==s3));
System.out.println("s3=s1"+(s3==s1));
System.out.println("s5=hello"+(s5 =="hello"));
System.out.println("s6=hello"+(s6 =="hello"));
System.out.println(s1.equals(s2));
System.out.println(s3.equals(s1));
System.out.println(s3.equals(s4));
输出结果:
s1=s2 true
s1=hello true
s3=s4 false
s5=s3 true
s3=s1 false
s5=hello false
s6=hello false
true
true
true
是不是觉得一脸懵逼,摸不着头脑?
看了下面string两种赋值方法你一定会茅塞顿开
要知道String的两种赋值方法:
① " "直接赋值法
② new关键字法
第一种直接赋值法Jvm会先去常量池中检测该字符串是否存在,若存在不创建直接地址指向,共享此字符串,否则创建新的字符串,类似Integer的自动装箱,
第二种new创建的会直接在堆中开辟内存空间
所以s2和s1都指向常量池中hello字符串
而s3和s4分别在堆中开辟了内存,分别指向不同地址
s3==s1前者内存为堆后者为常量池,自然有s3==s1为false
包装类部分
注意Integer的享元模式:-128~127之间的数值在自动装箱的过程中,如果内存中存在相同数值的Integer类对象,则会共享此对象
看完下面的部分你就会明白
首先要知道包装类的自动装箱和自动拆箱机制
大家一定都见过这个语句Integer i= 5;
在jdk1.5以前,这样的代码是错误的,必须要通过Integeri = newInteger(5);这样的语句实现;而在jdk1.5以后,Java提供了自动装箱的功能,只需Integer i= 5; JVM会帮我们自动装箱,即基本数据类型转为包装类
相应的,包装类到基本数据类型的过程就是拆箱;如
Integeri =5;
intj =i;//自动拆箱
底层源码:
装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的xxxValue方法实现的。(xxx代表对应的基本数据类型)
注意:
Integer i=new Integer(xxx) 不会触发自动装箱的过程
Integeri=xxx;才会触发;
例如
Integer i1= new Integer(100);//手动装箱
Integer i2= newInteger(100);
System.out.println("i1=i2" + (i1 == i2));//false
System.out.println("i1.equals(i2)"+i1.equals(i2));//true
符合我们的预期,因为i1和i2是不同的对象不同的地址
Integer i1= 200;//自动装箱
Integer i2= 200;
System.out.println("i1=i2" + (i1 ==i2));//false
符合我们的预期,因为i1和i2是不同的对象不同的地址
Integer i1= 100;
Integer i2= 100;
System.out.println("i1=i2" + (i1 ==i2));//true
好奇怪?换个数值结果竟然不一样?难道i1和i2是同一个对象吗?
结论:此时的i1和i2指向同一对象,原因是Integer比较特殊,在自动装箱过程中,对于-128~127的值,它们被装箱为Integer对象后,会存在内存中被重用,始终只存在一个对象,而如果超过-128~127的值,被装箱后的Integer对象并不会被重用,相当于new一个新的Integer对象
另外包装类==基本数据类型 比较的也是值
Integer i1= 200;
Integer i2= 100;
Integer i3= i2+100;//结果300不在-128~127,自动装箱时产生新的对象
System.out.println("i1=i2+i0" + (i1 == i3));//false
int i=200;
System.out.println("i1=i"+(i1==i));//true
System.out.println("i1=i2+100"+(i1==i2+100));//true
因为i2+100运算完得到200,还没有来得及自动装箱就和i1进行了比较
包装类例:
Integer i1= newInteger(100);
Integer i2= newInteger(100);
Integer i3= 200;
Integer i4= 200;
Integer i5= 100;
Integer i6= 100;
System.out.println(i1.equals(i2));
System.out.println("i1=i2" + (i1 == i2));
System.out.println("i3=i4" + (i3 == i4));
System.out.println("i5=i6" + (i5 == i6));
System.out.println("i5=i6+0"+(i5==i6+0));
输出结果:
true
i1=i2 false
i3=i4 false
i5=i6 true
i5=i6+0 true
3.继承Object的类
== 比较的是地址
equals()比较的是地址,没有重写Object类中的equals(),比较两个对象的引用是否相等
例如
Object obj1=new Object();
Object obj2=new Object();
System.out.println(obj1==obj2);
System.out.println(obj1.equals(obj2));
StringBuffer s1=new StringBuffer();
StringBuffer s2=new StringBuffer();
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
输出结果:
false
false
false
false
equals()和hashcode ()的比较:
当比较大量数据的时候hashcode()比equals()效率高
比如向hashset(不允许重复)集合中有1000个元素,添加新元素的时候
equals:调用这个元素的equals()方法,遍历所有元素依次对比,没有重复的话添加
hashcode:调用这个元素的hashcode()方法,定位到相映的物理位置上
如果没有元素,它就可以直接存储在这个位置上,不用再进行任何比较
如果已经有元素了,就调用equals方法与新元素进行比较,相同则不存,不相同的话,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将这些元素串在一起
equal()相等的两个对象他们的hashCode()肯定相等
hashCode()相等的两个对象他们的equal()不一定相等,因为可能会发生Hash散列值冲突
所以当有大量元素需要进行比较的时候先用hashcode()比较,不相等则两个元素不等。如果hashcode()相等,再调用equals()比较,如果equal()也相同,则表示这两个元素相同。