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

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

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

SetHash:一个是无序不重复的集合,你知道为什么吗?
因为SetHash根据hashCode返回值和equals来判断两个对象是否相同(不止地址还指内容)
在HashCode的返回值判断插入位置,equals判断该位置上是否有和要插入的元素相同的
所以要保证SetHash插入的元素不重复,那就要重写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() 可以同时完成两份工作  排序和消除重复。


能帮到你们,我十分荣幸

展开阅读全文

没有更多推荐了,返回首页