重写hashCode()方法

      hashCode()用于返回调用该方法的对象的散列码值,此方法将返回整数形式的散列码值。

      在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了。一旦一个类重写equals()方法,通常也会重写hashCode()方法。

      下面是重写hashCode()方法的约定的内容,来自Object规范[JavaSE6]:

       (1)、在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一直。

      (2)、如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。

      (3)、如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生不同的整数结果。但是程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。

       总之只有一个目的:hashCode方法要为"不相等的对象产生不相等的散列吗"。 下面我给出一个反例的代码。

 

import java.util.HashSet;

public class User {
    //用户名称
    private String userName;
    //用户密码
    private String password;
    
    public User(String userName, String password){
        this.userName = userName;
        this.password = password;
    }
    
    /**
     * 覆盖equals方法
     */
    public boolean equals(Object obj){
        if(obj == null){
            return false;
        }
        //如果是同一个对象返回true,反之返回false
        if(this == obj){
            return true;
        }
        //判断是否类型相同
        if(this.getClass() != obj.getClass()){
            return false;
        }
        User user = (User)obj;
        return userName.equals(user.userName) && password.equals(user.password);
    }
    
    public String toString(){
        //System.out.println(userName.hashCode() + "      oooo");
        return userName + ":" + password;
    }
    
    public static void main(String[] args) {
        HashSet<User> set = new HashSet<User>();
        User user01 = new User("xiaom", "1");
        User user02 = new User("xiaom", "1");
        User user03 = new User("xiaoh", "2");
        User user04 = new User("xiaoj", "3");
        set.add(user01);
        set.add(user02);
        set.add(user03);
        set.add(user04);
        
        System.out.println("对象的内容" + "\t\t" + "对象的散列码");
        for(User user : set){
            System.out.println(user.toString() + "\t\t" + user.hashCode());
        }
    }
}

 

 

 

      输出的结果:

对象的内容	对象的散列码
xiaoj:3		17510567
xiaom:1		6413875
xiaoh:2		827574
xiaom:1		21174459

      这时候,我们可能期望只打印三个对象的值。由于我们重写了equals方法,所以user01对象和user02的对象是相等的。那么在hashSet集合中就应该只有三个元素。这是为什么呢?

      Java 中的集合类有两类:一类是List,一类是Set。前者允许集合内的元素可 以重复,后者则不允许集合中的元素可以重复。那么Java是如何判断集合中的元素是否重复呢?是通过对象中的equals()方法进行比较。但是,如果每增加一个元素,都要进行一次equals比较。那么当集合中有很多元素时,就要进行很多次比较。也就是说,如果集合中有10000个元素,就得调用 10000次equals。这很明显降低了效率。于是,Java就采用了哈希表的原理,将数据依据指定的算法映射到一个地址上。这样一来,当集合要添加一个元素时,只要调用该对象的hashCode方法,就可以找到指定的物理地址。如果该地址上没有元素,那么就将元素放在该地址上,不用再进行其他任何比较 了。如果该地址上有元素,那就调用equals方法进行比较,如果相同,则不需要添加改元素;如果不相同,就散列到其他地址。这样一来,实际调用 equals方法的次数就大大地减少了。

      我们再回过头看看刚刚的小例子。会出现那种错误,是因为我们没有重写hashCode方法。HashSet调用的是Object中的hashCode方法,而Object中的hashCode方法返回的是对象的地址值,所以HashSet认为user01对象与user02对象是不相等的,才在hashSet集合中出现4个对象。现在我们就来重写一下hashCode方法,看看效果如何,代码如下:

import java.util.HashSet;

public class User {
    //用户名称
    private String userName;
    //用户密码
    private String password;
    
    public User(String userName, String password){
        this.userName = userName;
        this.password = password;
    }
    
    /**
     * 覆盖equals方法
     */
    public boolean equals(Object obj){
        if(obj == null){
            return false;
        }
        //如果是同一个对象返回true,反之返回false
        if(this == obj){
            return true;
        }
        //判断是否类型相同
        if(this.getClass() != obj.getClass()){
            return false;
        }
        User user = (User)obj;
        return userName.equals(user.userName) && password.equals(user.password);
    }
    
    /**
     * 重写toString
     */
    public String toString(){
        return userName + ":" + password;
    }
    
    /**
     * 重写hashCode
     */
    public int hashCode(){
        int result = 17;
        result = 31 * result + userName.hashCode();
        result = 31 * result + password.hashCode();
        return result;
    }

    public static void main(String[] args) {
        HashSet<User> set = new HashSet<User>();
        User user01 = new User("xiaom", "1");
        User user02 = new User("xiaom", "1");
        User user03 = new User("xiaoh", "2");
        User user04 = new User("xiaoj", "3");
        
        set.add(user01);
        set.add(user02);
        set.add(user03);
        set.add(user04);
        System.out.println("对象的内容" + "\t\t" + "对象的散列码");
        for(User user : set){
            System.out.println(user.toString() + "\t\t" + user.hashCode());
        }
    }
}

 

      输出的结果:

对象的内容        对象的散列码
xiaom:1        -759483308
xiaoh:2        -759483462
xiaoj:3        -759483399

      执行结果正确,收工。

     由于本人能力有限,博客中的内容难免会有错误。请各位朋友评判指出,别让我一错再错,哈。非常感谢!

 

posted on 2013-03-12 20:09  free9277  阅读(1592)  评论(0编辑  收藏  举报

导航