对于所有对象都通用的方法
equals
浅谈==和equals的区别
先引入一段代码
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
int n=3;
int m=3;
System.out.println(n==m);
String str = new String("hello");
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1==str2);
str1 = str;
str2 = str;
System.out.println(str1==str2);
}
}
这里的结果为true false true。
n==m结果为true,这个很容易理解,变量n和变量m存储的值都为3,肯定是相等的。而为什么str1和str2两次比较的结果不同?要理解这个其实只需要理解基本数据类型变量和非基本数据类型变量的区别。
在Java中数据类型分为基本数据类型和引用数据类型。基本数据类型存储的值,比较的就是值得本身。像代码中的n和m,就是属于基本数据类型,两者都存储3,所以比较结果是相同的。引用数据类型存储的不是值本身,而是其关联的对象在内存中的地址,比较的是内存地址。
简单的说,==是比较对象的值。但是不同数据类型存储值的类型是不同的。
那么equals比较的又是什么
看下equals的源码
public boolean equals(Object obj) {
return (this == obj);
}
equals用来比较两个对象的引用是否相等
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1.equals(str2));
两个new出来的字符串对象应该是不同的对象,但是这里输出的结果为true
看下Sring类对equals的覆写
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
这里覆写equals,直接判断两个字符串是否相等
自定义Student类
public boolean equals(Object obj) {
if(this==obj) {
return true;
}
if(obj instanceof Student) {
Student student=(Student)obj;
if(this.getName()==student.getName() &&
this.getAge()==student.getAge()){
return true;}
}
return false;
}
覆盖equals时总要记得覆盖hashCode
如果两个对象根据equals方法比较是相等的,那么调用这两个对象中任意一个的hashCode方法都必须产生同样的值。相等的对象必须具有相等的hashCode。如果不覆写hashCode,就会导致无法结合所有基于散列的集合一起正常运作,比如HashMap,HashSet,HashTable。
hashCode作用
hashCode()的作用是获取散列码,为一个int型整数,是this对象在哈希表中的索引位置。
我们都知道,散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!
散列表的本质是通过数组实现的。当我们要获取散列表中的某个“值”时,实际上是要获取数组中的某个位置的元素。而数组的位置,就是通过“键”来获取的;更进一步说,数组的位置,是通过“键”对应的散列码计算得到的。
假设,HashSet中已经有1000个元素。当插入第1001个元素时,需要怎么处理?因为HashSet是Set集合,它允许有重复元素。
“将第1001个元素逐个的和前面1000个元素进行比较”?显然,这个效率是相等低下的。散列表很好的解决了这个问题,它根据元素的散列码计算出元素在散列表中的位置,然后将元素插入该位置即可。对于相同的元素,自然是只保存了一个。
由此可知,若两个元素相等,它们的散列码一定相等;但反过来确不一定。在散列表中,
1、如果两个对象相等,那么它们的hashCode()值一定要相同;
2、如果两个对象hashCode()相等,它们并不一定相等。
注意:这是在散列表中的情况。在非散列表中一定如此
hashCode和equals
假设类重写了equals(),但是没有重写hashCode。这两个对象equals结果正确,但是hashCode返回的依然是不同地址的值。
在向HashSet集合中存入一个元素时,HashSet会调用该对象(存入对象)的hashCode()方法来得到该对象的hashCode()值,然后根据该hashCode值决定该对象在HashSet中存储的位置。简单的说:HashSet集合判断两个元素相等的标准是:两个对象通过equals()方法比较相等,并且两个对象的HashCode()方法返回值也相等。如果两个元素通过equals()方法比较返回true,但是它们的hashCode()方法返回值不同,HashSet会把它们存储在不同的位置,依然可以添加成功,然而这并不是我们想要的。
如何覆写hashCode
- 把某个非零的常数值,比如说7,保存在一个名叫result的int型变量
- 对于对象中每个关键的域f(属性),完成以下步骤,计算每个域对应得散列码
- 如果该域是boolean类型,则计算(f?1:0)
- 如果该域是byte,char,short或者int类型,则计算(int)f
- 如果该域是long类型,则计算(int)(f^(f>>>32))
- 如果该域是float类型,则计算Float.floatToInitBits(f)
- 如果该域是对象引用,则调用该对象得hashCode()
public int hashCode() {
int result = 7;
result=result+age;
result=result+Integer.valueOf(name);
return result;
}