0
点赞
收藏
分享

微信扫一扫

关于hashCode与equals



首先我得说明,在我们自己写得类中你可以复写这两个方法,此时从语法的角度来说,他们没关系。
在object中

public native int hashCode();

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

两个准则

在java集合中


判定两个对象是否相等需要以下两步;


1 hashCode的值是否相等,


  如果不相等,那么不用说,两个对象肯定不相等;如果hashCode的值相等,那么看第二步;


2 equals的值是否相等;


  如果equals的值相等,那么两个对象相等;


  若是equals的值不相等,那么两个对象不相等;


(我们可以看到,equals起最后的作用)


那就有一个问题了,既然equals起最后的作用我们在集合类中判断两个对象是否相等的时候,就直接调用equals即可,为什么还有hashcode呢?


因为有的时候equals方法比较复杂,我们如果通过hashcode方法已经说明两个对象不相等了就可以不用调用equals了


hashCode是个本地方法,返回的结果是对对象存储位置的一系列复杂运算;

而equals就是单纯的比较两个对象的地址值是否相等;


看到这里就存在几个准则


如果equals相等,那么hashCode一定相等;  地址值已经相等了,再怎么计算它都是相等的


如果hashCode相等了,equals不一定相等;  4%3=1  1%3=1 看到了吧,不同的值经过相同的运算法则,有可能取得相同的值



代码说明

首先我们看看字符串的equals与hashCode


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;
}


/**
* Returns a hash code for this string. The hash code for a
* <code>String</code> object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using <code>int</code> arithmetic, where <code>s[i]</code> is the
* <i>i</i>th character of the string, <code>n</code> is the length of
* the string, and <code>^</code> indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return a hash code value for this object.
*/
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;

for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}



在读hashCode方法的注释的时候我一直不明白


从数学上说,这两个有什么关系

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]         //n代码字符串长度  ^代码乘方或者说幂

for (int i = 0; i < value.length; i++) {

h = 31 * h + val[i];

}


后来我验证了一下,两种方法返回的int值是一样的 为什么?不明觉厉!


     下面这个例子的结果我已经写到注释里了

String s1=new String("zhaoxudong");
String s2=new String("zhaoxudong");
String s3="zhaoxudong";
String s4="zhaoxudong";
System.out.println(s1==s2); //两个new出来的对象 s1 s2里面放的是两个不同对象的地址 自然不相等
System.out.println(s1==s3); //false 一个指向堆内存 一个指向常量池
System.out.println(s3==s4); //true 都在常量池中
System.out.println("##### ");
System.out.println(s1.equals(s2));//true
System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode()等于s3.hashcode()
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());

Set hashset=new HashSet();
hashset.add(s1);
hashset.add(s2);
Iterator it=hashset.iterator();
while(it.hasNext())
System.out.println(it.next()); //只打印出一个



再看这个


import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;

public class hashCode
{
public static void main(String[] args)
{

HashSet hs=new HashSet();
hs.add(new Student(1,"zhangsan"));
hs.add(new Student(2,"lisi"));
hs.add(new Student(3,"wangwu"));
hs.add(new Student(1,"zhangsan"));

Iterator it=hs.iterator();
while(it.hasNext())
System.out.println(it.next());
}
}
public class Student
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public String toString()
{
return num+":"+name;
}
}



运行结果


两个张三?

1:zhangsan

3:wangwu

1:zhangsan

2:lisi


因为,hash的add方法在加入新元素的时候,先找Student的hashCode方法,结果没有,那就调用object的hashCode方法,两个zhangsan都是new出来的,自然不是一个对象喽,所以就加进去了!


怎么改?


给stuend类中加入以下两个方法


public int hashCode()
{
return num*name.hashCode();
}
public boolean equals(Object o)
{
Student s=(Student)o;
return num==s.num && name.equals(s.name);
}



但是,大家得记住,equals相等,hashcode就一定相等这个准则,所以两个方法不要乱写!



改完之后,就只有一个zhangsan了。


参考资料

​​http://www.iteye.com/topic/257191​​

举报

相关推荐

0 条评论