hashCode()与equals()

在Java中,hashCode()与equals()都是用来比较两个对象是否相等的,那么两者之间有什么区别和联系呢,既然equals()已经实现了此功能,为什么还要用hashCode()呢?此篇文章,就这个问题,简略的说明一下啊,欢迎各位大牛补充。

一、equals()

默认情况下(没有覆盖equals()),equals()都是调用Object类下的equals(),而Object的equals()主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)。

没有覆盖equals()代码如下:

public class Student {  
    private int age;  
    private String name;  

    public Student() {  
    }  
    public Student(int age, String name) {  
        super();  
        this.age = age;  
        this.name = name;  
    }  
    public int getAge() {  
        return age;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}

测试代码如下:

public class EqualsTest {  
    public static void main(String[] args) {  
        List<Student> list = new ArrayList<Student>();  
        Set<Student> set = new HashSet<Student>();  
        Student stu1  = new Student(18,"张三");  
        Student stu2  = new Student(18,"张三");  
        System.out.println("stu1 == stu2 : "+(stu1 == stu2));  
        System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));  
        list.add(stu1);  
        list.add(stu2);  
        System.out.println("list size:"+ list.size());  

        set.add(stu1);  
        set.add(stu2);  
        System.out.println("set size:"+ set.size());  
    } 
}

运行结果如下:

stu1 == stu2 : false
stu1.equals(stu2) : false
list size:2
set size:2

结果分析:

Student类没有覆盖equals(),stu1调用的equals()实际上调用的是Object的equals(),采用的是对象内存地址是否相等来判断对象是否相等。因为这两个对象都是新的对象,所以内存地址是不相等的,所以stu1.equals(stu2)是false。

覆盖equals()代码如下:

public class Student {  
    private int age;  
    private String name;  

    public Student() {  
    }  
    public Student(int age, String name) {  
        super();  
        this.age = age;  
        this.name = name;  
    }  
    public int getAge() {  
        return age;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    @Override  
    public boolean equals(Object obj) {  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Student other = (Student) obj;  
        if (age != other.age)  
            return false;  
        if (name == null) {  
            if (other.name != null)  
                return false;  
        } else if (!name.equals(other.name))  
            return false;  
        return true;  
    }  
} 

运行结果如下:

stu1 == stu2 : false
stu1.equals(stu2) : true
list size:2
set size:2 

结果分析:

因为Student两个对象的age属性和name属性相等,而且又是通过覆盖的equals()来判断,所以stu1.equals(stu2)为true。

二、hashCode()

在Object类中有一个方法,public native int hashCode()。哈希码相比大家都不陌生,哈希码并非是唯一的,它是一种算法,让同一个类的对象按照自己不用的特征尽量拥有不同的哈希码,但是这并不代表不同的对象所拥有的哈希码一定完全不同,也会存在相同的情况,所以这个取决于程序员如果写哈希码的算法。

几个常用的哈希码的算法:

1、Object类的hashCode:返回对象的内存地址经过处理后的结构,由于每个对象的内存地址都不一样,所以哈希码也不一样。
2、String类的hashCode:根据String类包含的字符串的内容,根据一种特殊的算法返回哈希码,只要字符串所在的堆空间一致,返回的哈希码也一样。
3、Integer类的hashCode:返回的哈希码就是Integer对象里所包含的那个整数的值,例如Integer i1 = new Integer(10),i1.hashCode的值就是10。所以,2个一样大小的Integer对象,返回的哈希码也一样。

hashCode方法的主要作用就是为了配合基于散列的集合一起正常运行。这样的散列的集合包含了HashSet、HashMap、HashTable。

可以考虑这样的一种情况,当我们想集合中插入对象的时候,如何判定集合中是否已经存在该对象?

也许绝大多数的人都会想到调用equals()来逐个的进行比较,这个方法不是不行,也是一种可行的方案,但是如果在集合中已经存在了一万条,十万条甚至更多的数据的时候,如果采用equals()方法去逐一的比较,那必然会造成的是一个需要考虑的另外一个问题,那就是效率问题。此时,hashCode()的作用就有用了,如果在集合中没有该hashCode的值,那简单了,直接存进去,不用再进行任何的比较。如果存在了该hashCode的值,则再去调用equals()与新的元素进行比较,如果一致的话,就不存了,如果不一致,那就散列它的地址,所以这里还存在一个冲突解决的问题。这样一来的话,实际调用equals()的次数就会大大地降低,说的大白话一点,就是说:Java中的hashCode()就是根据一定的规则将与对象有关的信息(比如对象的存储地址、对象的属性等等)映射成一个数值,这个数值被称作为散列值。

延续使用上面的代码,此次覆盖hashCode()并不覆盖equals(),hashCode码是通过age和name生成的。

覆盖hashCode()后的Student类:

public class Student {  
    private int age;  
    private String name;  

    public Student() {  
    }  
    public Student(int age, String name) {  
        super();  
        this.age = age;  
        this.name = name;  
    }  
    public int getAge() {  
        return age;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    @Override  
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + age;  
        result = prime * result + ((name == null) ? 0 : name.hashCode());  
        return result;  
    }     
}

运行结果:

stu1 == stu2 : false
stu1.equals(stu2) : false
list size:2
hashCode :786954
hashCode :786954
set size:2 

结果分析:

此次只覆盖了hashCode()并没有覆盖equals(),两个对象虽然hashCode一样,但是在将stu1和stu2放入set集合时由于equals()比较两个对象是false,所以就没有再比较两个对象的hashCode值。

覆盖equals()和hashCode():

public class Student {  
    private int age;  
    private String name;  
    public Student() {  
    }  
    public Student(int age, String name) {  
        super();  
        this.age = age;  
        this.name = name;  
    }  
    public int getAge() {  
        return age;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    @Override  
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + age;  
        result = prime * result + ((name == null) ? 0 : name.hashCode());  
        System.out.println("hashCode : "+ result);  
        return result;  
    }  
    @Override  
    public boolean equals(Object obj) {  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Student other = (Student) obj;  
        if (age != other.age)  
            return false;  
        if (name == null) {  
            if (other.name != null)  
                return false;  
        } else if (!name.equals(other.name))  
            return false;  
        return true;  
    }  
} 

运行结果:

stu1 == stu2 : false
stu1.equals(stu2) :true
list size:2
hashCode :786954
hashCode :786954
set size:1

结果分析:

stu1和stu2通过equals()比较相等,而且返回的hashCode值一样,所以放入set集合中时只放入了一个。

equals()相等,hashCode值不相等代码如下:

public class Student {  
    private int age;  
    private String name;  
    private static int index=5; 
    public Student() {  
    }  
    public Student(int age, String name) {  
        super();  
        this.age = age;  
        this.name = name;  
    }  
    public int getAge() {  
        return age;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    @Override  
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + (age+index++);  
        result = prime * result + ((name == null) ? 0 : name.hashCode());  
        System.out.println("result :"+result);
        return result;  
    }  
    @Override  
    public boolean equals(Object obj) {  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Student other = (Student) obj;  
        if (age != other.age)  
            return false;  
        if (name == null) {  
            if (other.name != null)  
                return false;  
        } else if (!name.equals(other.name))  
            return false;  
        return true;  
    }  
}

运行结果:

stu1 == stu2 : false
stu1.equals(stu2) : true
list size:2
hashCode :776098
hashCode :776129
set size:2

结果分析:

虽然stu1和stu2通过equals()比较相等,但两个对象的hashCode值不相等,所以在将stu1和stu2放入set集合时认为是两个不同的对象。

修改stu1的某个属性值:

public class Student {  
    private int age;  
    private String name;  
    public Student() {  
    }  
    public Student(int age, String name) {  
        super();  
        this.age = age;  
        this.name = name;  
    }  
    public int getAge() {  
        return age;  
    }  
    public String getName() {  
        return name;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    @Override  
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + age;  
        result = prime * result + ((name == null) ? 0 : name.hashCode());  
        System.out.println("hashCode : "+ result);  
        return result;  
    }  
    @Override  
    public boolean equals(Object obj) {  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Student other = (Student) obj;  
        if (age != other.age)  
            return false;  
        if (name == null) {  
            if (other.name != null)  
                return false;  
        } else if (!name.equals(other.name))  
            return false;  
        return true;  
    }  

} 

测试代码:

public class EqualsTest {  
    public static void main(String[] args) {  
        List<Student> list = new ArrayList<Student>();  
        Set<Student> set = new HashSet<Student>();  
        Student stu1  = new Student(3,"张三");  
        Student stu2  = new Student(3,"张三");  
        System.out.println("stu1 == stu2 : "+(stu1 == stu2));  
        System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));  
        list.add(stu1);  
        list.add(stu2);  
        System.out.println("list size:"+ list.size());  

        set.add(stu1);  
        set.add(stu2);  
        System.out.println("set size:"+ set.size());  
        stu1.setAge(34);  
        System.out.println("remove stu1 : "+set.remove(stu1));  
        System.out.println("set size:"+ set.size());  
    } 
} 

运行结果:

stu1 == stu2 : false
stu1.equals(stu2) : true
list size:2
hashCode : 775943
hashCode : 775943
set size:1
hashCode : 776904
remove stu1 : false
set size:1

结果分析:

当我们将某个对象存入set中时,如果该对象的属性参与了hashCode值的计算,那么以后就不能修改该对象参与hashCode计算的那些属性,否则会引起意想不到的错误,正如上述代码中,不能移除stu1对象。

总结:

1、equals()用于比较对象的内容是否相等(覆盖之后);
2、hashCode()只有在集合中用到;
3、覆盖equals()时,比较对象相等将通过覆盖后的equals()进行比较,判断对象的内容是否相等;
4、将对象放入集合时,首先判断要放入对象的hashCode值与集合中任意一个元素的hashCode值是否相等,如果不相等,则直接将该对象放入集合中,如果hashCode值相等,再通过equals()判断要放入的对象与集合中的任意一个对象是否相等,如果equals()判断不相等,直接将该元素放入到集合中,否则不放入。

另外,有些人会误以为,默认情况下,hashCode返回的就是对象的存储地址,事实上这种看法是不全面的,有些JVM在实现时是直接返回对象的存储地址,但是大多数的时候并不是这样的,只能说可能存储地址有一定的关联。

有些人可能又会问,可以直接根据hashCode值判断两个对象是否相等吗?答案肯定是不可以的。因为不同的对象可能会生成相同的hashCode值。虽然我们不能根据hashCode值判断两个对象是否相等,但是可以直接根据hashCode值判断两个对象不等,如果两个对象的hashCode值不等,则必定是两个不同的对象。如果要判定两个对象是否真正的相等,则必须通过equals()。也就是说,对于两个对象:

1、如果equals()得到的结果为true,则两个对象的hashCode值必定相等;
2、如果equals()得到的结果为false,则两个对象的hashCode值不一定不同;
3、如果两个对象的hashCode值不等,则equals()得到的结果必定为false;
4、如果两个对象的hashCode值相等,则equals()得到的结果是未知的。

附录两段代码:

A:HashMap的put()实现:

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

B:HashSet的add()实现:

public boolean add(E e) {  
    return map.put(e, PRESENT)==null;  
    }

最后,欢迎程序员的朋友大家互相交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值