1."=="和equals的区别
==的操作符生成的是一个boolean类型的结果,比较的是两个数值之间的关系或者对象的引用是否相等。equals的本意是判断两个对象的内容是否相等,但一般判断的时候分两种情况。
在Java中有八种基本的数据类型,对于这八种的基本数据类型,变量在存储的时候是直接存储的值,所以在用==来比较这些基本数据类型时候,比较的是变量的值是否相等,对于下面的程序,我们都知道返回的是true和false。
int a=1;
int b=1;
int c=2;
System.out.println(a==b);
System.out.println(a==c);</span>
在Java中,引用类型的变量存储的并不是值本身,而是与其关联的对象在内存中的地址,所以当==操作引用型的变量时候,比较的是两边的对象的引用是否相同,即内存地址。对于下面的程序,将会输出true和false。
Student stu1=new Student("A",12);
Student stu2=stu1;
Student stu3=new Student("B",12);
System.out.println(stu1==stu2);
System.out.println(stu1==stu3);
总结,对于==操作符而言,如果比较的基本数据类型,则直接比较值即可,如果比较的是引用型数据类型则比较的是内存中的地址是否相同。
equals方法是Java中Object类的一个方法,在Object类中是这样定义equals方法的
public boolean equals(Object obj) {
return (this == obj);
}
可以看到在Object中,equals的作用和==是一样的,但是我们知道对于下面的程序来说,输出为true。
String a="Hello";
String b="Hello";
System.out.println(a.equals(b));
这是为什么呢,因为在String类中重写了equals方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
从重写的方法中可以看到,首先判断对象的引用是否相同,再判断比较的对象是否内容一致,如果一致的话就比较二者的内容是否相同,所以对于上述的程序,输出为true就不奇怪了。在Java中,所有的内置类的equals方法都是这样的。
还是上面的例子接着看下面的代码:
Student stu1=new Student("A",12);
Student stu2=stu1;
Student stu3=new Student("A",12);
System.out.println(stu1.equals(stu2));
System.out.println(stu1.equals(stu3));
上述的输出为true和false,因为在Student类中没有重写equals方法,所以在用equals方法比较stu1和stu2 的时候比较的是引用,在比较stu1和stu3时候还是引用,因为后两者指向的对象在内存中的地址不一样,所以结果就是false,但是stu1和stu3很显然是两个一样的Student,内容是一样的,所以在用equals进行比较时候,希望返回的true。
因此我们就需要在定义的Student类的时候重写equals方法,这样在使用equals方法的时候比较的就是两者的内容是否相同了。
public boolean equals(Object o){
if (o==null){
return false;
}
if(o==this){
return true;
}
if(getClass()!=o.getClass()){
return false;
}
Student s=(Student)o;
return (this.getId()==s.getId());
}
}
重写了equals方法之后,再进行比较的值为true
2.Hashcode和equals
在Java中的Hashcode方法也是有Object类定义的,该方法会针对不同的对象返回一个不同的整数。Hashcode方法主要应用于散列表中。在程序运行期间,如果根据equals方法判断两个对象时相等的,那么这两个对象的hashcode方法的返回值肯定也是要相等的。
我们知道在Java中引用到Hash表的地方有集合中的Hashset,HashMap和HashTable
在Set中不允许元素相同,Map中不允许键值相同。如果在集合中插入1000个元素,再插入1001个元素的时候要一个个的进行比较吗,如果是这样的话,那么集合的效率也太低了,这里用到了哈希表,在进行元素插入时候,先根据插入的元素计算对应的hash值,根据hash值可以直接定位到元素在hash表中的位置 ,然后从该位置向后在链表中进行查找,这样可以提高插入和查询的效率。
所以如果利用equals方法比较两个元素的结果为true的话,那么二者的hashcode方法返回值也要是一样的,如果equals方法返回的值是false,那么二者的hashcode方法返回值可以相同也可以不相同,如果hashcode值不同,那么二者的equals方法肯定是false。hashcode值得结果只能决定元素在hash表中的位置。
如果我们在没有覆写hashcode的方式下将两个内容相同的对象插入到Hashset中,查看输出的效果(已经覆写了equals方法):
Student stu1=new Student("A",12);
Student stu2=stu1;
Student stu3=new Student("A",12);
System.out.println(stu1.equals(stu2));
System.out.println(stu1.equals(stu3));
HashSet<Student> s=new HashSet<Student>();
s.add(stu1);
s.add(stu3);
System.out.println(s.size());</span>
上面程序的结果输出如下:可以看到,集合的大小为2,但插入的两个元素明明是一样的,我们希望在这种情况下集合的大小因该是1,此时就需要覆写hashcode方法了。
public int hashCode(){
return name.hashCode();
}</span>
此时的输出结果为:
因为在没有覆写hashcode方法之前,默认调用的是本地的hashcode方法,此时计算的两者的hashcode值是不一样的,所以在hash表中的位置也就是不一样的,所以就可以插入两个,但覆写hashcode方法之后,二者计算得到的hashcode值是一样的,所以在插入进行判断的过程中就会判断两者的内容是否一样,最后结果为1.因此一旦重写了equals方法,就必须要重写hashcode方法。