关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »Java中Equals使用整理

    Java中Equals使用整理

    2015-03-04 00:00:00 出处:投稿
    分享

    前段时间一直在工作中使用Java,由于有一些C++功底,于是简单看了一下Java相关的语法便开始编写代码,结果在创建一个自定义类,并将自定义类放入ArrayList中,之后查找ArrayList是否有此元素的时候,发现怎么也查询不到对应的元素。在网上搜了一下资料,发现原因是没有重写对象的equals()方法,导致无法查找到对应的对象。之后由查了与之联系的相关资料,便有了以下的总结。

    这篇总结的形式是提出个问题,然后给出问题的答案。这是目前学习知识的一种尝试,可以让学习更有目的。

    Q1.什么时候应当重写对象的equals方法?

    答:一般在我们需要进行值比较的时候,是需要重写对象的equals方法的。而例外情况在《effective java》的第7条“在改写equals的时候请遵守通用约定”中清楚描述了。

    我们知道,在Java中,每个对象都继承于Object.如果不重写,则默认的equals代码如下所示:

    public boolean euqals(Object obj){
        return this == obj;
    }

    由上面的代码可以看出,equal默认是使用“==”来判断两个对象是否相等。两个对象使用“==”比较的是对象的地址,只有两个引用指向的对象相同的时候,“==”才返回true。所以,在开头的例子中,就需要重写equals方法,让两个对象有equals的时候。

    Q2.如何重写equals?

    答:首先,当改写equals方法时,需要保证满足它的通用约定。这些约定如下所示:

    自反性,对于任意的引用值x,x.equals(x)一定为true。 对称性,对于任意的引用值x和y,当且仅当y.equals(x)时,x.equals(y)也一定返回true. 传递性,对于任意的引用值x,y,z。如果x.equals(y)返回true,y.euqals(z)返回true,则x.equals(z)也返回true。 一致性,对于任意的引用值x和y,如果用于equals比较的对象信息没有修改,那么,多次调用x.equals(y)要么一致返回true,要么一致返回false. 非空性,所有的对象都必须不等于null。

    其实我觉的一个简单的方法是参照String的equals方法即可,官方出版,满足各种要求。其代码如下所示

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = count;
            if (n == anotherString.count) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = offset;
                int j = anotherString.offset;
                while (n– != 0) {
                    if (v1[i++] != v2[j++])
                        return false;
                }
                return true;
            }
        }
        return false;
    }

    函数的解释如下所示:

    使用==检查“实参是否是指向对象的一个引用”。 使用instanceof检查实参是否和本对象同类,如果不同类,就不相等。 将实参转换为正确的类型。 根据类的定义,检查实现此对象值相等的各个条件。

    更详细的信息,还是请看《effective java》的第7条“在改写equals的时候请遵守通用约定”。

    Q3.修改equals时需要注意什么?

    答:大致需要注意以下几点:

    若修改equals方法,也请修改hashCode方法

    首先这个是语言的一个约定,这么做的一个原因是当此对象作为哈希容器的元素时,需要依赖hashCode,对象默认的hashCode是返回一个此对象特有的hashCode,不同的对象的hashCode返回值是不一样的,而哈希容器处理元素时,是按照对象的哈希值将对象分配到不同的桶中,若我们不重写对象的hashCode,那么值相等的对象产生的哈希值也会不同,这样当在哈希容器中查找时,会找不到对应的元素。

    更详细的信息请看《effective Java》的第8条“改写equals时总是要改写hashCode”。

    重写时保证函数声明的正确

    请注意equals的声明是

    public boolean equals(Object obj)

    参数类型是Object,如果参数类型是此对象类型的话,如下:

    class Point{
    final int x;
    final int y;
    public void Point(int x, int y)
        this.x = x;
        this.y = y;
    }
    public boolean euqals(Point obj){
             return (this.x == obj.x && this.y == obj.y);
        }
    }

    下面代码执行是按照我们的预期执行的。

    Point a(1, 2);
    Poinr b(1, 2);
    System.out.println(a.equals(b));// 输出true

    但是如果将类A放入容器中,则会出问题

    import java.util.HashSet;
    
    HashSet<Point> coll = new HashSet<Point>();
    coll.add(a);
    System.out.println(coll.contains(b));// 输出false

    这是由于HashSet中的contains方法中调用的是equals(Object obj),而Point中的equals(Object obj)仍是Object的equals,这个方法在前面已经说过了,比较的是对象的地址,所以在coll中调用contains(b)时,当然得不到true。

    当有继承关系时注意equals的正确
    当一个类重写equals方法后,另一个类继承此类,此时,可能会违反前面说到的对称性,代码如下所示:

    public class ColoredPoint extends Point { 
        private final Color color;
        public ColoredPoint(int x, int y, Color color) {
            super(x, y);
            this.color = color;
        }
    
        @Override 
        public boolean equals(Object other) {
            boolean result = false;
            if (other instanceof ColoredPoint) {
                ColoredPoint that = (ColoredPoint) other;
                result = (this.color.equals(that.color) && super.equals(that));
            }
            return result;
        }
    }

    当我们作比较时

    Point p = new Point(1, 2);
    ColoredPoint cp = new ColoredPoint(1, 2, Color.RED);
    System.out.println(p.equals(cp)); //输出ture
    System.out.println(cp.equals(p)); //输出false

    原因是当调用Point.equals的时候,只比较了Point的x和y坐标,同时ColoredPoint也是Point类型,所以上面第三行代码相等,而调用ColoredPoint的时候,Point不是ColoredPoint类型,这样就导致第四行代码输出false。

    若我们忽略Color的信息来比较呢,例如将ColoredPoint的equals方法改为:

    @overwrite
    public boolean equals(Object obj){
        if((obj instanceof Point)){
            return false;
        }
    
        if(!(obj instanceof ColoredPoint)){
            return obj.equals(this);
        }
    
        return super.equals(obj) && ((ColoredPoint)obj).color == color;
    }

    这样就保证了对称性,但是却违反了传递性,即下面的情况:

    ColoredPoint cp1 = new ColoredPoint(1, 2, Color.RED);
    Point p = new Point(1, 2);
    ColoredPoint cp2 = new ColoredPoint(1, 2, Color.BLUE);
    System.out.println(cp1.equals(p)); //true
    System.out.println(p.equals(cp2)); //true
    System.out.println(cp1.equals(cp2)); //false

    面对这种情况,大致有两种解决方案,一种酷壳的文章–如何在Java中避免equals方法的隐藏陷阱的最后一条,断绝了Point和ColoredPoint相等的可能,这是一种处理方法,认为Point和ColoredPoint是不同的。另一种方法是effective Java上提出的,使用聚合而不是继承,将Point作为ColoredPoint的一个成员变量。目前我倾向于这种方法,因为聚合比继承更灵活,耦合更低。这种方法的代码如下所示:

    class ColoredPoint{
        private final Point point;
        private final Color color;
    
        public Point asPoint(){
            return point;
        }
    
        public boolean equals(Object obj){
            boolean ret = false;
            if(obj instanceof ColoredPoint){
                ColoredPoint that = (ColoredPoint)obj;
                ret = that.point.equals(point) && color.equals(that.color);
            }
            return ret;
        }
    }

    当ColoredPoint需要比较坐标时,可以调用asPoint方法来转化为坐标进行比较。其他情况比较坐标和颜色,这样就可以解决上面关于对称性和传递性的问题了。

    以上就是全文的内容,由于水平有限,文章中难免会有错误,希望大家指正。谢谢

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Edge浏览器百度被劫持/篡改怎么办,地址后边跟着尾巴#tn=68018901_7_oem_dg

    Google Chrome 在 iPhone 上新增了 Safari 数据导入选项

    Windows 11专业版 KMS工具激活产品密钥的方法

    DEDECMS安全策略官方出品

    Microsoft Text Input Application 可以关闭吗?

    新版本QQ如何关闭自带的浏览器?

    C++编程语言中continue的用法和功能,附举例示范代码

    c++ map 的数据结构、基本操作以及其在实际应用中的使用。

    C语言如何避免内存泄漏、缓冲区溢出、空指针解引用等常见的安全问题

    C语言中的break语句详解

    IT头条

    马斯克2026最新采访总结:2040年,全球机器人数量将突破100亿台

    23:52

    专家解读|规范人工智能前沿业态健康发展的新探索:解读《人工智能拟人化互动服务管理暂行办法》

    00:54

    用至强 6高存力搞定MoE卸载!

    17:53

    美国将允许英伟达向中国“经批准的客户”出售H200 GPU

    02:08

    苹果与微信就15%手续费达成一致?腾讯未置可否

    22:00

    技术热点

    PHP 和 Node.js 的10项对比挑战

    Javascript闭包深入解析及实现方法

    windows 7、windows 8.1手动增加右键菜单功能技巧

    MYSQL出错代码大汇总

    windows 7假死机怎么办 windows 7系统假死机的原因以及解决方法

    Ubuntu(Linux)下配置IP地址的方法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。