equals() vs hashCode()

1.深入理解equals()

  • 上一篇中“==” 和“equals()”的区别中讲到:Object类中的equals方法和“==”是一样的,没有区别,即两个对象的比较是比较他们的栈内存中存储的内存地址。而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==”不同。
public class TestEquals {
  public static void main(String[] args) {
    /**
     * 这里使用构造方法Cat()在堆内存里面new出了两只猫,
     * 这两只猫的color,weight,height都是一样的,
     * 但c1和c2却永远不会相等,这是因为c1和c2分别为堆内存里面两只猫的引用对象,
     * 里面装着可以找到这两只猫的地址,但由于两只猫在堆内存里面存储在两个不同的空间里面,
     * 所以c1和c2分别装着不同的地址,因此c1和c2永远不会相等。
     */
    Cat c1 = new Cat(1, 1, 1);
    Cat c2 = new Cat(1, 1, 1);
    System.out.println("c1==c2的结果是:"+(c1==c2));//false
    System.out.println("c1.equals(c2)的结果是:"+c1.equals(c2));//false
  }
}

class Cat {
  int color, weight, height;

  public Cat(int color, int weight, int height) {
    this.color = color;
    this.weight = weight;
    this.height = height;
  }
}

画出内存分析图分析c1和c2比较的结果,当执行Cat c1 = new Cat(1,1,1); Cat c2 = new Cat(1,1,1); 之后内存之中布局如下图:


image
  • 因此我们看出,当我们new一个对象时,将在内存里加载一份它自己的内存,而不是共用!对于static修饰的变量和方法则保持在方法区中,只加载一次,不会再多copy一份内存。
  • 所以我们在判断两个对象逻辑上是否相等,即对象的内容是否相等不能直接使用Object类的equals()方法,我们必须得重写equals()方法,改变这个方法默认的实现。下面在Cat类里面重写这个继承下来的equals()方法:
public class Cat {

    private int color;
    private int weight;
    private int height;

    public Cat(int color, int weight, int height) {
        this.color = color;
        this.weight = weight;
        this.height = height;
    }

    /**
     * 这里是重写了从Object类继承下来的equals()方法,改变这个方法默认的实现
     * 通过我们自己定义的实现来判断决定两个对象在逻辑上是否相等
     * 这里我们定义如果两只猫的color,weight,height都相同
     * 那么我们就认为这两只猫在逻辑上是一模一样的,即这两只猫是"相等"的
     * */
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        } else {
            /**
             * instanceof 是对象运算符
             * 对象运算符用来测定一个对象是否属于某个指定类或者指定的子类的实例
             * 如果左边的对象是右边的类创建的对象,则运算结果为true,否则为false
             * */
            if (obj instanceof Cat) {
                Cat c = (Cat) obj;
                if (c.color == this.color && c.weight == this.weight && c.height == this.height) {
                    return true;
                }
            }
        }
        return false;
    }
}

  • 设计思路很简单:先判断比较对象是否为null -->判断比较对象是否为要比较类的实例 --> 比较两个成员变量是否完全相等
  • 这样通过在类中重写equals()方法,我们可以在同一个类下比较不同对象是否相等了。

2.Hash算法原理以及HashCode深入理解

  • Java中的Collection有两类,一类是List,一类是Set。List内的元素是有序的,元素可以重复。Set元素无序,但元素不可重复。要想保证元素不重复,两个元素是否重复应该依据什么来判断呢?用Object.equals方法。但若每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说若集合中已有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。于是Java采用了哈希表的原理。
  • 当Set接收一个元素时根据该对象的内存地址算出hashCode,看它属于哪一个区间,在这个区间里调用equeals方法。【特别注意】这里需要注意的是:当俩个对象的hashCode值相同的时候,Hashset会将对象保存在同一个位置,但是他们equals返回false,所以实际上这个位置采用链式结构来保存多个对象。


    image
  • 上面方法确实提高了效率。但面临一个问题:若两个对象equals相等,但不在一个区间,因为hashCode的值在重写之前是对内存地址计算得出,所以根本没有机会进行比较,会被认为是不同的对象。所以JAVA对于equals方法和hashCode方法是这样规定的:
  1. 如果两个对象相同,那么它们的hashCode值一定要相同。也告诉我们重写equals方法,一定要重写hashCode方法,也就是说hashCode值要和类中的成员变量挂上钩,对象相同--> 成员变量相同 --> hashCode值一定相同
  2. 如果两个对象的hashCode相同,它们并不一定相同,这里的对象相同指的是equals方法比较。

http://blog.csdn.net/jiangwei0910410003/article/details/22739953

你可能感兴趣的:(equals() vs hashCode())