资讯专栏INFORMATION COLUMN

java equals 和 hashcode

HollisChuang / 2799人阅读

摘要:简单的一句话结论就是保证你定义的的对象拥有相同的。方法实现等价关系自反性。即一个对象必须等于其自身。对于任意引用值和返回真时,也一定为真传递性。发现一样,调用方法继续比较,发现个对象一样。

简单的一句话结论就是:保证你定义的“equal”的对象拥有相同的hash code。
1)两个对象equals()为真,则它们的hashCode() 一定相同
2)两个对象hashCode()相同,equals()不一定为真这两个Object方法如果没有很好的定义,可能会产生使用者不希望看到的效果。
2)实现hashCode(),原则如本答案第一句话所示,并尽量使用对象自有特性作为生成因子以减小冲突的概率。

equals方法实现等价关系
1、自反性。
对应任意引用值x,x.equals(x),一定为真。
即一个对象必须等于其自身。
2、对称性。
对于任意引用值x和y,x.equals(y)返回真时,y.equals(x)也一定为真
3、传递性。
对于任意引用值x、y和z,x.equals(y)返回真,且y.equals(z)返回真,则x.equals(z)一定返回真。
4、一致性。
对于任意引用值x和y,如果用于equals比较对象的信息没有被修改的话,那么多次调用x.equals(y)要么都是true,要么都是false
5、对于任意的非空引用值x,x.equals(null) 一定返回false

下面的例子违反了对称性

package equals;

import java.util.ArrayList;
import java.util.List;

public final class CaseInsensitive {

    private String s;

    public CaseInsensitive(String s) {
        if(s == null) {
            throw new UnsupportedOperationException();
        }
        this.s = s;
    }

    @Override
    public boolean equals(Object o) {
        if(o instanceof CaseInsensitive) {
            return s.equalsIgnoreCase(((CaseInsensitive) o).s);
        }
        // 如果传进来的是String对象
        if(o instanceof  String) {
            return s.equalsIgnoreCase(((String)o)) ;
        }
        return false;
    }

    public static void main(String[] args) {
        CaseInsensitive o = new CaseInsensitive("CSP");
        String s = "csp";
        System.out.println(o.equals(s));// 返回true
        System.out.println(s.equals(o));;// 返回 false
        
        // 根据对称性,o 与 s 返回true,那么 s 与 o 也应该返回true,而这里却返回false,违反了对称性。原因是String的equals方法,不知道要忽略大小写
       
       //如果将o加入集合中,则返回false,而我们的原意是想让他返回true
        List list = new ArrayList();
        list.add(o);
        System.out.println(list.contains(s)); // 返回false 
    }
}

下面例子违反传递性

package equals;

/**
 * Created by Administrator on 2017/9/23.
 */
public class Point {
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public boolean equals(Object o) {
        if(!(o instanceof Point)) {
            return false;
        }
        Point p = (Point) o;
        return p.x == x && p.y == y;
    }
}
package equals;

public class ColorPoint extends Point {

    private String color;

    public ColorPoint(int x, int y,String color) {
        super(x, y);
        this.color = color
    }

    public boolean equals(Object o) {
        if(!(o instanceof ColorPoint)) {
            return false;
        }
        ColorPoint p = (ColorPoint) o;
        return super.equals(o) && p.color.equals(color);
    }

    public static void main(String[] args) {
        ColorPoint cp = new ColorPoint(1,1,"red");
        Point p = new Point(1,1);
        System.out.println(cp.equals(p)); //false
        System.out.println(p.equals(cp)); //true
        // 违反了对称性。因为ColorPoint 与 Point 比较时,多了个color属性
    }
}

我们可以对ColorPoint进行修改,让其满足对称性,在混合比较时,忽略颜色信息

....
public boolean equals(Object o) {
        if(!(o instanceof Point)) {
            return false;
        }
        //对象是Point类型,不是ColorPoint,进行父类级别的比较
        if(!(o instanceof ColorPoint)) {
            return o.equals(this);
        }
        ColorPoint p = (ColorPoint) o;
        return super.equals(o) && p.color.equals(color);
    }
    ....
public static void main(String[] args) {
        ColorPoint cp = new ColorPoint(1,1,"red");
        Point p = new Point(1,1);
        ColorPoint cp1 = new ColorPoint(1,1,"black");
        System.out.println(cp.equals(p)); //true
        System.out.println(p.equals(cp1)); //true
        System.out.println(cp.equals(cp1)); //false
        // 违反了传递性
    }

以上代码虽然符合对称性,但是却违反了传递性

想要解决以上问题,只能使用复合来代替继承

public class ColorPoint {

private String color;
private Point p;
public ColorPoint(String color,Point p) {
    this.color = color;
    this.p = p;
}

public Point getP() {
    return p;
}

@Override
public boolean equals(Object o) {
    if(!(o instanceof ColorPoint)) {
        return false;
    }
    ColorPoint cp = (ColorPoint) o;
    return  cp.p.equals(p) && cp.color.equals(color);
}

}

当你重写了equals()时,也应该重写hashcode();

来看当一个类重写了equals()方法时,没有重写hashCode方法,并将其放入Map中

import java.util.HashMap;
import java.util.Map;

public final class CaseInsensitive {

private int x;
private int y;
private String s;

public CaseInsensitive(int x,int y,String s) {
    this.s = s;
    this.x = x;
    this.y = y;
}

@Override
public boolean equals(Object o) {
    if(o == this) {
        return true;
    }
    if(!(o instanceof CaseInsensitive) || o == null) {
        return false;
    }
    CaseInsensitive cp = (CaseInsensitive) o;
    return cp.s.equals(s) && cp.x == x && cp.y == y;
}

public static void main(String[] args) {
    CaseInsensitive c = new CaseInsensitive(1,1,"cc");
    CaseInsensitive c1 = new CaseInsensitive(1,1,"cc");
    Map m = new HashMap();
    m.put(c,"c");
    System.out.println(m.get(new CaseInsensitive(1,1,"cc"))); // null
}

}

按照我们的意思,应该是返回 c 的,但是实际上返回的是 null

原因在于,没有重写hashCode()方法,Map在放入元素时,是先根据对象的散列值进行存放,如果散列值一样,那么则继续比较equals方法。此处默认是Object的hashCode方法。所有2个对象不一样。

让我们重写hashCode方法试下

....
@Override

public boolean equals(Object o) {
    if(o == this) {
        return true;
    }
    if(!(o instanceof CaseInsensitive) || o == null) {
        return false;
    }
    CaseInsensitive cp = (CaseInsensitive) o;
    return cp.s.equals(s) && cp.x == x && cp.y == y;
}

//此处使用IDEA 默认生成的hashCode
@Override 
public int hashCode() {
    int result = x;
    result = 31 * result + y;
    result = 31 * result + (s != null ? s.hashCode() : 0);
    return result;
}

public static void main(String[] args) {
    CaseInsensitive c = new CaseInsensitive(1,1,"cc");
    CaseInsensitive c1 = new CaseInsensitive(1,1,"cc");
    Map m = new HashMap();
    m.put(c,"c");
    System.out.println(m.get(new CaseInsensitive(1,1,"cc"))); //返回c
}

此时就可以获取到c了。因为我们重写了hashCode()方法,比较2个对象的hashCode。发现hashCode一样,调用equals()方法继续比较,发现2个对象一样。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70517.html

相关文章

  • Java Object类方法分析

    摘要:类方法分析声明文章均为本人技术笔记,转载请注明出处类简介类是所有类的父类,在中只有基本数据类型不是对象。对于所有数组类型对象类型基本数据类型数组都继承于类方法类中通过判断两个对象是否具有相同引用,从而判断两个对象是否相同子类只要重写方法,就 Java Object类方法分析 声明 文章均为本人技术笔记,转载请注明出处https://segmentfault.com/u/yzwall ...

    zhisheng 评论0 收藏0
  • 【码艺杂谈】Java中的相同与不同

    摘要:如果根据方法得到两个对象不相同,那么两个对象的方法的结果不一定不相同,我们可以利用这一点来提高散列表的性能。最后回到文章开头的问题,如何判断两个对象或值是否相同这个问题其实有两方面的含义,一方面是判断的方法,另一方面是判断的效率。 Java中有很多场景需要判断两个对象或者两个值,那么 判断是否相同的依据是什么? 如何判断是否相同呢? 为了解释这个问题,我们从Java语言的根说起,那...

    xingqiba 评论0 收藏0
  • Java equals == 完全解析

    摘要:所以在对象没有重写这个方法时,默认使用此方法,即比较对象的内存地址值。结果为可以发现不管对象的内存地址是否相同并不影响其结果,所以类型比较的是数据值而不是内存地址值。 showImg(https://segmentfault.com/img/bVbqpku?w=800&h=344); 今天朋友突然问到一个问题: 两个对象使用x.equals(y)判断结果为true时,两个对象的hash...

    mikasa 评论0 收藏0
  • Java Object对象的hashcodeequals方法

    摘要:在中对象是一切对象都会自动继承的一个类,在这个类中定义的属性和方法可以说是每个类都必须的。这里有必要说说这里对象里面的几个方法返回该对象的哈希码值。这些基于表的集合,只能要求被存放的对象实现自己的方法,保证的均匀性。 Object 在Java中Object对象是一切对象都会自动继承的一个类,在这个类中定义的属性和方法可以说是每个类都必须的。 这里有必要说说这里对象里面的几个方法 has...

    chnmagnus 评论0 收藏0
  • java中的hashCode

    摘要:相关的文章网上很多了写这个主要是按自己的思路进行记录是什么中的实现是一个本地方法生成一个表征当前对象实例的特征值具体的实现根据的实现可能会不同中实际计算的函数的实现如下为时是直接使用的内存地址但默认使用的是的随 hashcode相关的文章网上很多了, 写这个主要是按自己的思路进行记录 hashCode是什么 Object中的hashCode实现是一个本地方法, 生成一个表征当前对象实例...

    cnTomato 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<