文档章节

Java 覆盖equals和hashCode方法

wuxinshui
 wuxinshui
发布于 2017/02/28 11:11
字数 1441
阅读 4
收藏 0
点赞 0
评论 0

前言

覆盖equals方法看起来似乎很简单,但是有许多覆盖方式会导致错误,并且后果非常严重,最容易避免这类问题的办法就是不覆盖equals方法。

什么时候需要覆盖equals方法?如果类具有自己特有的“逻辑相等”概念(不同于对象等同),而且超类还没有覆盖equals方法以实现期望的行为,这时需要覆盖equals方法。

覆盖equals

覆盖equals方法时,必须遵守它的通用约定,如果你违反了它们,就会发现你的程序将表现不正常,甚至奔溃,而且很难找到失败的根源。

通用约定

  1. 自反性。对于任何非null的引用值x、x,equals(x)必须返回true。
  2. 对称性。对于任何非null的引用值x、y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
  3. 传递性。对于任何非null的引用值x、y、z,如果x.equals(y)返回true,并且y.equals(z)返回true,则x.equals(z)必须返回true。
  4. 一致性。对于任何非null的引用值x、y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。
  5. 非空性。对于任何非null的引用值x、x,equals(null),必须返回false。

一般IDE工具,如IntelliJ IDEA可以帮助实现equals方法覆盖。基本上是符合以上约定的。

这里写图片描述

实现高质量equals方法的诀窍

  1. 使用==操作符检查“参数是否为这个对象的引用”。
  2. 使用instanceof操作符检查“参数是否为正确的类型”。
  3. 把参数转化为正确的类型。
  4. 对于该类的中每个关键域,检查参数中的域是否与该对象中对应的域想匹配。

示例

public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    @Override
    public boolean equals(Object obj) {
        if (this==obj) return true;
        if (!(obj instanceof Point)) {
            return false;
        }
        Point p = (Point) obj;
        return p.x == x && p.y == y;
    }
}

类型检查为什么不用getClass()

getClass测试代替instanceof 测试,只有当对象有相同的实现时,才能使对象等同。此外,使用instanceof 还可以省去对null的判断。

    @Override
    public boolean equals(Object obj) {
        if (this==obj) return true;
        if (obj==null || this.getClass()!=obj.getClass()) return false;
        Point p = (Point) obj;
        return p.x == x && p.y == y;
    }

Point1,Point2继承Point

    public static void point1ComparePoint2(){
        Point point1=new Point1(1,2);
        Point point2=new Point2(1,2);
        Point point3=new Point2(1,2);

        //不同实现的比较
        System.out.println(point1.equals(point2));
        System.out.println(point2.equals(point1));

        //相同实现的比较
        System.out.println(point2.equals(point3));

    }

运行结果:
false
false
true

覆盖hashCode方法

重写equals方法必须要重写hashCode方法。如果不这样做的话会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作。如HashMap、HashTable、HashSet。

    public static void pointListCompare(){
        Point point1=new Point(1,2);
        Point point2=new Point(1,2);
        Map<Point,String> mp=new HashMap<>();
        mp.put(point1,"Point1");
        System.out.println("point1==point2 "+(point1==point2));
        System.out.println("point1 hashCode is:"+(point1.hashCode()));
        System.out.println("point2 hashCode is:"+(point1.hashCode()));
        System.out.println(mp.containsKey(point1));
        System.out.println(mp.containsKey(point2));
    }

运行结果:
point1==point2 false
point1 hashCode is:2133927002
point2 hashCode is:1836019240
true
false

Point中添加hashCode()方法

    @Override
    public int hashCode() {
        //31是一个奇素数,有个很好的特性,乘法可以优化为移位和减法:31*i==(i<<5)-i。
        // 现在的VM可以自动完成这种优化。习惯上都使用素数来计算散列结果
        int result = x;
        result = 31 * result + y;
        return result;
    }

运行结果:
point1==point2 false
point1 hashCode is:33
point2 hashCode is:33
true
true

HaspMap有一项优化,可以将与每个项相关联的散列码缓存起来,如果散列码不匹配,也不必检验对象的等同性。

完美实例

不同类型的覆盖方法和hashCode生成。

public class PerfectEquals {
    //基本类型
    private int x;
    private char c;
    private double d;
    private float f;
    private String name;//引用类型
    private Color color;//枚举类型
    private Point point;//引用类型
    private List<Point> pointList;//集合类


    public PerfectEquals(int x, char c, double d, float f, String name, Color color, Point point, List<Point> pointList) {
        this.x = x;
        this.c = c;
        this.d = d;
        this.f = f;
        this.name = name;
        this.color = color;
        this.point = point;
        this.pointList = pointList;
    }

    @Override
    public boolean equals(Object o) {
        //1. 使用==操作符检查“参数是否为这个对象的引用”。
        if (this == o) return true;
        //2. 使用instanceof操作符检查“参数是否为正确的类型”。
        if (!(o instanceof PerfectEquals)) return false;
        //3. 把参数转化为正确的类型。
        PerfectEquals that = (PerfectEquals) o;

        //4. 对于该类的中每个关键域,检查参数中的域是否与该对象中对应的域想匹配。
        if (x != that.x) return false;
        if (c != that.c) return false;
        if (Double.compare(that.d, d) != 0) return false;
        if (Float.compare(that.f, f) != 0) return false;
// if (name != null ? !name.equals(that.name) : that.name != null) return false;//10000000 times compare cost time is 27-32ms
        if (name!=that.name|| name!=null&&!name.equals(that.name)) return false;//更快些,10000000 times compare cost time is 3-4ms
        if (color != that.color) return false;
// if (point != null ? !point.equals(that.point) : that.point != null) return false;
        if (point!=that.point||point!=null&&!point.equals(that.point)) return false;
// return pointList != null ? pointList.equals(that.pointList) : that.pointList == null;
        return pointList==that.pointList|| pointList!=null&&pointList.equals(that.pointList);
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = x;
        //31是一个奇素数,有个很好的特性,乘法可以优化为移位和减法:31*i==(i<<5)-i。
        // 现在的VM可以自动完成这种优化。习惯上都使用素数来计算散列结果
        result = 31 * result + (int) c;
        temp = Double.doubleToLongBits(d);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (f != +0.0f ? Float.floatToIntBits(f) : 0);
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + (color != null ? color.hashCode() : 0);
        result = 31 * result + (point != null ? point.hashCode() : 0);
        result = 31 * result + (pointList != null ? pointList.hashCode() : 0);
        return result;
    }

    public static void main(String[] args) {
        List<Point> pointList=new ArrayList<>();
        Point point1=new Point(1,2);
        Point point2=new Point(2,2);
        Point point3=new Point(3,2);
        pointList.add(point1);
        pointList.add(point2);
        pointList.add(point3);

        PerfectEquals pe1=new PerfectEquals(12,'c',12D,21f,"Perfect",Color.RED,new Point(23,24),pointList);
        PerfectEquals pe2=new PerfectEquals(12,'c',12D,21f,"Perfect",Color.RED,new Point(23,24),pointList);

        long start=System.currentTimeMillis();
        for (int i=0;i<10000000;i++){
            pe1.equals(pe2);
        }
        long end=System.currentTimeMillis();
        System.out.println("compare cost time is :"+(end-start));
    }

© 著作权归作者所有

共有 人打赏支持
wuxinshui
粉丝 2
博文 64
码字总数 32603
作品 0
普陀
程序员
hashCode与equals的区别与联系

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

期待变强的菜鸟
2014/04/09
0
1
java的HashCode方法(转载)

有许多人学了很长时间的Java,但一直不明白hashCode方法的作用, 我来解释一下吧。首先,想要明白hashCode的作用,你必须要先知道Java中的集合。   总的来说,Java中的集合(Collection)有...

hanzhankang
2014/01/24
0
0
Java问答:终极父类(2)—下篇

哈希码 问: hashCode()方法是用来做什么的? 答: hashCode()方法返回给调用者此对象的哈希码(其值由一个hash函数计算得来)。这个方法通常用在基于hash的集合类中,像java.util.HashMap,j...

LCZ777
2014/05/22
0
0
scala中的==、equals、eq

scala中equals方法和==是检查值是否相等,而eq方法检查的是引用是否相等。 Scala 的==与Java的有何差别 Java 里的既可以比较基本类型也可以比较引用类型。对于基本类型,Java 的==比较 值比较...

张欢19933
04/27
0
0
图说 Java:理解 Java 机制最受欢迎的 8 幅图

世间总是一图胜过千万言,下面的8幅图来自于 Program Creek 的 Java教程 ,目前这是该网站最受欢迎的文章. 希望本文能帮你回顾你已经知道的那些知识。如果图片讲解的不够清晰,你可能需要阅读详...

大数据之路
2013/10/30
0
3
我理解的== 、equals 、hashcode

先来回顾一下 == 与equals 的区别 ==是运算符 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean ,他们之间的比较,应该用...

CTO搬砖达人
06/26
0
0
Java之hashCode()和equals()方法

程序世界和现实世界其实是一样的,相等和相同是不同的概念,就此简要说明一下其中的含义: 1 equals() 的作用是什么? 2 equals() 与 == 的区别是什么? 3 hashCode() 的作用是什么? 4 hash...

董广明
2014/04/01
0
3
Java 基础:hashCode方法

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 一、前言 泥瓦匠最近被项目搞的天昏地暗。发现有些要给自己一些目标,关于技术的目标: 专注很重要。专注Java 基础 + H5...

泥沙砖瓦浆木匠
2015/04/27
0
4
byte[]作为key存储在HashSet中

byte[]作为key存储在HashSet中 hashCode和equals方法 当使用ide进行开发时,最简单的重写就是用ide自动生成hashCode和equals方法 例如: package hashcode; /** * Created with IntelliJ ID...

秋风醉了
2014/07/02
0
0
java类的hashcode()剖析

hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有 例如内存中有这样的位置 0 1 2 3 4 5 6 7 而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,...

街头诗人
2012/10/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Service Mesh所应对的8项挑战

Lori Macvittie 微服务架构是把双刃剑,我们享受它带来的开发速度(development velocity),却也不得不面对服务间通讯带来的复杂性问题。 目前大多数扩展容器化微服务的架构多是基于proxy-b...

好雨云帮
16分钟前
0
0
时间复杂度

1. 维基上的定义 在计算机科学中,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低...

liuyan_lc
22分钟前
0
0
js中的~符

~是js里的按位取反操作符,~~就是执行两次按位取反,其实就是保持原值,但是注意虽然是原值,但是对布尔型变量执行这个操作,会转化成相应的数值型变量,也就是 ~~true === 1,~~false === 0...

JamesView
23分钟前
0
0
webpack安装

npm install --save-dev webpack-cli

Vincent-Duan
25分钟前
0
0
实时监听EditText内容变化

主要是addTextChangedListener方法的使用 aswerEdittext.addTextChangedListener(new TextWatcher() { //编辑框的内容发生改变之前的回调方法 @Override public void before...

王先森oO
28分钟前
0
0
python连接mysql数据库

安装好必要的pymysql插件 pip install pymysql 写好配置文档 DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), #......

南桥北木
30分钟前
0
0
linux下安装 Zookeeper

Zookeeper 注册中心安装 安装: wget http://www.apache.org/dist/zookeeper/zookeeper-3.3.3/zookeeper-3.3.3.tar.gztar zxvf zookeeper-3.3.3.tar.gzcd zookeeper-3.3.3cp conf/zoo_......

颖辉小居
32分钟前
0
0
C#判断文件的真实类型(非扩展名判断)

public static void CheckFileType() { string path = @"D:\word.doc"; System.IO.FileStream fs = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read); ......

Lytf
33分钟前
0
0
java - 把日志生成到指定目录

1.按天小时生成日志文件 log4j.appender.ruiming.DatePattern='_'yyyy-MM-dd-HH 2.最大日志文件数据 log4j.appender.ruiming.MaxBackupIndex=100 3.下图画圈圈的很关键,是生成到知道目录必备...

轻量级赤影
34分钟前
0
0
少走弯路,给Java 1~5 年程序员的建议

今天LZ是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈每个阶段要学习的内容甚至是一些书籍。这一部分的内容,同样适用于一些希望转行到Java的同学。 在大家看之前,LZ要先声...

Java填坑之路
34分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部