关于equals、hashcode和集合类的小结

一.首先明确一点:equals()方法和hashcode()方法是Object类里的方法

  查看源码可以知道,在Object类中equals(obj)方法直接返回的是  this == obj 的值。对于引用变量来说,这个式子判断的是变量中存储的内存地址是否一致。而hashcode()方法的出现,目的是为了提高哈希表的性能,也就是说,提供这样一个方法是为了提高依据hashcode进行存取的数据结构的性能的。

 两者的关系:

 在Object类的注释中可以看到两个原则:

 两个类如果通过equals()方法比较返回的值为true,则这两个类调用hashcode()方法返回的整数值必须相等。

 两个类如果通过equals()方法比较返回的值为false,则这两个类调用hashcode()方法返回的整数值建议不同(hashcode的初衷),可以相等。

   故在Object类中:

 equals()方法比较的方式是比较两个引用的内存地址,对于任何新 new 出来的对象来说,他们的内存地址都是不一样的。而Object类默认的hashcode计算方式和类的内存地址相关,所以他们的hashcode是不同的。

      

package com.njsteel.test;

public class Test {
    public static void main(String[] args){
        
        Object obj1 = new Object();
        Object obj2 = new Object();
        Object obj3 = obj1;
        
        obj1.hashCode();//14576877
        obj2.hashCode();//12677476
        obj3.hashCode();//14576877
        
        obj1.equals(obj2);//false
        obj1.equals(obj3);//true        
    }
}

 

  故,我们自定义的所有类,如果没有重写equals()方法和hashcode()方法,则都是满足上述的情况的。因为自定义的类都是Object类的子类。

 

二. 其次,Java提供的很多便捷类都会重写equals()和hashcode()方法。

   这里只说明一下String类的情况。

     为什么String类要重写equals()和hashcode()方法?

     一般来说,当我们想判断两个String类对象的内容是否相同时,我们就会调用equals()方法,这是API文档告诉我们的。我们比较时,一般是两个不同对象的引用。这时候,即使String对象的内容相同,如果按照Object类里equals()的判断方法判断,返回的值始终都是false。

    所以为了满足这样的需要,String类equals()方法重写添加了根据内容判断两个String对象内容是否相同的逻辑代码。

    equals()被重写后,hashcode()自然也要被重写,因为重写的equals()方法破坏了Object类中规定的两者的关系。于是String类里的hashcode()方法也重写成了和String内容相关的样子。以满足原来的关系。

    其他的类就可以按照这样的情况以此类推。。

三. 和某些集合类的关系

    先说Set:

    在看书的时候,对于Set的描述有:Set集合不允许包含相同的元素

    在Set里,相同的元素是指通过equals()方法返回值为true的两个对象。在调用Set的add()方法时,就会进行这个判断(add进来的元素会与Set集合里已经有的所有元素进行equals判断,如果有相同,则add()方法返回false,添加失败)。

  对于HashSet类:

  HashSet是Set接口的一个实现,自然也是不允许包含相同的元素。不过判断的方法和原始的Set有点不同。

  往HashSet集合里添加元素时,它不仅会通过equals()方法判断,还要调用hashcode()方法判断。只有当两个元素equals相等,并且hashcode也相等时,两个元素才是相同的元素。当其中有一个方法比较显示两个元素不一样时,HashSet会将其添加。

   对于HashMap类:

   我们知道HashMap的Key值是不能重复的。其实HashMap的Key值是保存在一个Set()集合里的。所以,作为Key,至少他们的hashcode是不相同的。如果两个对象是相同的Key(equals相等,hashcode也相同),值会覆盖。

 

验证代码:

 

package com.njsteel.test;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class HashSetTest {
    public static void main(String[] args){
        Set<MyString> set_1 = new HashSet<MyString>();
        MyString myString1 = new MyString(); 
        MyString myString2 = new MyString(); 
        //添加一个元素
        set_1.add(myString1);
        //添加一个equals返回true,但是hashcode不同的元素
        System.out.println(myString1.equals(myString2)+"!!!"+myString1.hashCode()+"/"+myString2.hashCode());
        System.out.println(set_1.add(myString2));//true,添加成功。
        
        
        //添加一个元素   
        Set<MyTestObject> set_2 = new HashSet<MyTestObject>();
        set_2.add(new MyTestObject());
        //添加一个equals返回false,但是hashcode相同的元素。
        System.out.println(set_2.add(new MyTestObject())+""+set_2.size());
        
        Map<MyString,String> map = new HashMap<MyString,String>();
        
        map.put(myString1, "1");
        map.put(myString2, "2");
        for(Object key : map.keySet()){
            System.out.println(map.get(key));
        }

    }
}

class MyString{
    
    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        return true;
    }

    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        return super.hashCode();
    }
    
    
}

class MyTestObject{

    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        return 1;
    }
    
}
 

 

 欢迎讨论指正。。

 

posted @ 2014-05-27 19:11  Derry.1990  阅读(497)  评论(0编辑  收藏  举报