我们学些java j2se的时候为还说比较两个引用是否值(内容)相等的时候不去重写hashcode方法,只是重写equals方法呢:
一下是单纯重写equals方法的例子:
/**
* 测试重写equals方法
* @author Rick
*
*/
public class EqualsUsage {
String name;
public EqualsUsage(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean equals(Object obj) {
String name = ((EqualsUsage)obj).getName();
return this.name == name;
}
}
以下是测试类:
/**
* 验证重写equals的重写方法
* @author Administrator
*
*/
public class Test {
public static void main(String[] args) {
EqualsUsage usage$1 = new EqualsUsage("Rick");
EqualsUsage usage$2 = new EqualsUsage("Rick");
EqualsUsage usage$3 = new EqualsUsage("Teddy");
System.out.println(usage$1.equals(usage$2));
System.out.println(usage$2.equals(usage$3));
}
}
输出结果:
true
false
由上面例子来讲,由于EqualsUsage的属性都是Rick,因此有两个引用的比较是true,因为我们重写了Object的equals方法,他们比较的是引用的属性值是否相等.
就好像两个筐装满水果的整体当作是硬盘,两个筐分别是不同的物理地址,第一个筐有苹果和菠萝,第二个筐有香蕉和菠萝
当我们没有重写equals方法的时候,我们默认调用Object类的equals方法,如果这样比较usage$1.equals(usage$2)
Object的equals方法如下:
/* @param obj the reference object with which to compare.
* @return {@code true} if this object is the same as the obj
* argument; {@code false} otherwise.
* @see #hashCode()
* @see java.util.HashMap
*/
public boolean equals(Object obj) {
return (this == obj);
}
就好像我们比较的是这两个筐的菠萝是不是在同一个筐(同一个物理地址)
如果现在我们的需求是,我们需要equals比较的是分别在这两个筐的菠萝到底是不是同一种水果(是不是同一对象),那我们就比较的就是它们的属性啦,是否名字相同,是否颜色,外形特征相同等等,重写的equals方法就是这么重写的.
但是他们的引用(地址)还是不会变的.
而在list和set等学习中,为什么不单只重写equals还要重写hashCode呢,原因如下
比如,一个"扒"字,如果在字典查找字典上对应的"扒",此时"pa"\"ba"这两个发音既是"扒"的属性,也是可以助你查找到"扒"字的索引。因此,当我们程序接收到"扒"字的时候,我们重写了equals获取到他的属性,知道操作的对象是什么,重写了hashcode,知道怎么去找到这个"字"的位置。
如下例子:
package com.j2se.hashCodeProblem;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
public class BasicContainer {
public static void main(String[] args) {
Collection c = new LinkedList();
c.add("hello");
c.add(new Name("f1","l1"));
c.add(new Integer(100));
c.add(new Name("f1","l1"));
System.out.println(c); //初始版本
/*c.remove("hello"); //不论一下有没有重写equals方法都会被删除,因为其本身已经重写equals方法,下同!!
c.remove(new Integer(100));
System.out.println(c.remove(new Name("f1","l1")));//没有重写hashcode是不能删除
System.out.println(c);*/
/*System.out.println(c);
System.out.println(new Integer(100));
System.out.println(new Integer(100) instanceof Integer);*/
//System.out.println(100 instanceof Integer); //编译有误
//System.out.println(true instanceof Boolean); //编译有误
/* System.out.println(new Boolean(true) instanceof Boolean);*/
System.out.println(c);
c.remove(new Name("f1","l1"));
System.out.println(c);
}
}
class Name implements Comparable<Name> {
private String firstName;
private String lastName;
public Name(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String toString() {
return firstName + " " + lastName;
}
/**
* 重写equals方法
*/
public boolean equals(Object obj) {
if (obj instanceof Name) {
Name name = (Name) obj;
return (firstName.equals(name.firstName))
&& (lastName.equals(name.lastName));
}
return super.equals(obj);
}
public int hashCode() {
return firstName.hashCode();
}
public int compareTo(Name name) {
Name n = (Name)o;
int lastCmp =
lastName.compareTo(n.lastName);
return
(lastCmp!=0 ? lastCmp :
firstName.compareTo(n.firstName));
}
}
可以再结合String源码对hashcode的具体实现进一步了解,
然而注意的是两个对象的hashcode相同,equals比较也返回true,他们的物理地址仍不一定相同.
总结:什么时候需要重写hashcode呢,就是当你的对象作为索引的时候!