list\set等容器(集合)那里重写equals为什么还要重写hashCode方法

时间:2022-11-16 16:00:45

我们学些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呢,就是当你的对象作为索引的时候!