深入详解HashSet的元素为什么要重写hashCode和equals方法

在Object这个类中hashCode是本地方法,它的值与对象在内存中的地址有关,所以不会存在两个hashCode返回值相同的对象,equals是比较对象的引用是否相等

hashCode方法的目的是什么呢? 
—它是为hash table中插入为提供hash数值

HashSet:一个是无序不重复的集合,你知道为什么吗? 
因为HashSet根据hashCode返回值和equals来判断两个对象是否相同(不止地址还指内容) 
在HashCode的返回值判断插入位置,equals判断该位置上是否有和要插入的元素相同的 
所以要保证HashSet插入的元素不重复,那就要重写hashCode和equals

我们先举一个没有重写hashCode和equals的例子吧

先定义一个元素类Point

class Point{
    private int a;
    private int b;
    public Point(int a,int b){
        this.a = a;
        this.b = b;
    }
    @Override
    public String toString(){
        return "("+a+","+b+")";
    }

再定一个Test类

public class TestHashSet {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        HashSet<Point> hash = new HashSet<Point>();
        hash.add(new Point(4,5));
        hash.add(new Point(4,5));
        hash.add(new Point(1,5));
        for(Point p:hash){
            System.out.println(p);
        }
    }

}

按理论上讲应该输出: 
(4,5),(1,5)这两个值,因为有重复的(4,5) 
但是实际输出却是这样:

为什么呢?因为没有重写hashCode导致两个(4,5)的插入位置不一样或者插入一样但是,两个对象的引用地址不一样

重写equals的目的是:让两个(4,5)引用地址不一样,但内容一样的对象是相等的 
重写hashCode的目的:让A.equals(B)为true的两个对象的hashCode返回值一样

在这里有可能有小伙伴有疑问?问可不可以不重写hashCode呢? 
答案是不可以,为什么?让我们有例子来解答疑惑

还是刚才个例子,我们重写了equals方法
 

class Point{
    private int a;
    private int b;
    public Point(int a,int b){
        this.a = a;
        this.b = b;
    }
    @Override
    public String toString(){
        return "("+a+","+b+")";
    }

    @Override
    public boolean equals(Object obj){

        if(this==obj)
            return true;
        if(!(obj instanceof Point))
            return false;
        Point p = (Point)obj;

        return this.a == p.a && this.b == p.b;

    }
}

 测试类

import java.util.HashSet;

public class TestSet {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        HashSet<Point> hash = new HashSet<Point>();
        Point p = new Point(4,5);
        Point p1 = new Point(4,5);
        hash.add(p);
        System.out.println(hash.contains(p1));
    }

}

你们猜结果为什么?应该是true吧 
但是输出却为false,为什么呢? 
因为p和p1的hashCode不一样就用可能不会插入到同一个位置,所以会返回false

因此使用HashSet 的add()方法插入元素的时候: 
|- HashSet会自动调用元素的hashCode()方法。 
|- 然后根据hashCode()方法的返回值 来决定元素要插入的位置。 
|- 如果该位置上已经存在元素了 则会调用该元素equals()方法进行比较。 
|- 如果两个元素相等 则丢掉欲插入的元素。 
|- 如果两个元素不相等 则新元素会被加入到另一个位置(通过冲突检测来决定哪一个位置),这样就消除了重复。 
|- 范例1中使用的是Point类 其并没有重写这2个方法。因此无法消除重复。 
|- 范例2中使用的是String类,在String类已经重写完了Object类的equals()和hashCode()方法,所以可以消除重复。

说白了: 
|- 如果想完整的使用HashSet类 那么最少要重写equals()和hashCode()方法。 
|- 重写hashCode() 用于获得元素的存储位置。 
|- 重写equals() 用于在两个元素的位置相同的时候 比较两个元素是否相等。

总结一下: 
Set接口有两个子类:HashSet和TreeSet 。 
|- HashSet 
|- 特点:在不存在重复元素的基础上,还可以进行高速的存取元素。 
|- 要求:需要为您的类重写hashCode()和equals()方法。 

|- TreeSet 
|- 特点:在不存在重复元素的基础上,还可以将元素自动排序。 
|- 要求:需要为您的类实现Comparable接口,并重写compareTo方法。 
|- 重写compareTo() 可以同时完成两份工作  排序和消除重复。

 

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值