'=='&equals&hashCode的深入理解

本文详细解析Java中基本数据类型与引用数据类型的差异,深入探讨==与equals方法的区别及应用场景,同时讲解如何正确重写equals与hashCode方法,确保对象一致性的同时提升集合操作效率。
1. Java数据类型
  • 基本数据类型:基本类型数据不存在“引用”概念,其值直接存储在内存中的内存栈的栈空间里面;
  • 引用数据类型:简单来说,“引用”是存储在内存的内存栈上,对象本身存储在内存堆上。
  • 举个例子:

    Object o = new Object();
    o 是存在于栈中指向堆里面的实例化对象 Object。此时 o 为 null,代表 o 指向为空,是没有地址存在的。但是 Object o = “”,o是有地址的,只是地址代表的内容为空。

2. == 与 equals() 的区别
  • 对于基本数据类型之间的比较,应用“==”进行比较,比较的是他们的值;

  • 对于引用数据类型,用“”进行比较,比较的是他们在内存中的存放地址。Java所有的类都是继承于Object基类,在Object中的基类中定义了一个equals()方法,这个方法的初始行为就是比较对象的内存地址。因此,对于没有重写 equals() 方法的类,他们之间还是比较的是内存地址的值,“”和equals() 是等价的。但我们常用的String、Integer、Date等这些类重写了equals() 方法,他们就不是比较内存地址了。案例如下:

    String str1 = "ab";
    String str2 = "cd";
    String str3 = "abcd";
    String str4 = "abcd";
    String str5 = new String("abcd");
    String str6 = new String("abcd");
    
    boolean b1 = str1 == str2; //false
    boolean b2 = (str1 + str2) == str3; //false
    boolean b3 = str4 == str3; //true
    boolean b4 = str5 == str6; //false
    boolean b5 = (str1 + str2).equals(str4); //true
    boolean b6 = str5.equals(str6); //true
    
3. 重写对象的 equals 方法

我们看下面这段代码

public class EqualsTest {
    public static void main(String[] args) {
        Cat cat1 = new Cat("red", "10", 2);
        Cat cat2 = new Cat("red", "10", 2);
        System.out.println(cat1 == cat2); //false
        System.out.println(cat1.equals(cat2)); //false
    }
}

class Cat {
    private String color;
    private String height;
    private int age;
    
    public Cat() {}
    public Cat(String color, String height, int age) {
        this.color = color;
        this.height = height;
        this.age = age;
    }
}

从前面我们所知道的“==”和 equals() 的定义,我们打印出来的结果都得为 false,事实也是如此。如果我们定义两只 Cat 的 color、height 和 age 相等时,那这两只 Cat 就是同一只。那么我们只能通过重写 Cat 类的 equals 方法来实现我们的需求。

@Override
public boolean equals(Object object){
    if (object == null) {
        return false;
    }
    if (object instanceof Cat) {
        Cat c = (Cat)object;
        if (c.getColor().equals(this.getColor()) && c.getHeight().equals(this.getHeight())&& c.getAge() == this.getAge()) {
            return true;
        }
    }
    return false;
}
4. equals 和 HashCode 详解

问题引入:我们都知道 Collection 下有个 Set 集合,它的元素是无序的且不可重复。当我们往里面存数据的时候,它是怎么保证元素不重复?或者说是依据什么来判断?如果我们使用 equals() 方法(被重写过),每增加一个元素就检查一次,当里面有成百上千个元素时,我们每增加一个元素就需要调用成百上千次 equals() 方法,这显然是不可取的,效率太低。

因此有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以算出一个哈希码,将哈希码分组并分别对应到储存区域,因此根据一个对象的哈希码就可以确定对象储存在哪个区域(我的理解是就像在一根绳子上挂灯笼,比如有5个节点可以挂灯笼,根据不同的灯笼样式挂在不同的节点,如果两个灯笼样式一致,但是却不是同一个,我们就把这个灯笼挂在上一个的下面)。因此简单的来说,当我们往 Set 集合中储存元素时,会先根据元素的 hashCode()方法获取元素的哈希码,然后找到对应储存区域,并通过 equals 方法与该区域内的其他元素进行比较;这样就可以不遍历集合中所有的元素来判重,进而提高效率。

那么问题来了,如果我们重写了对象的 equals() 方法,而不重写的它的 hashCode() 方法,那我们上述论述毫无意义;因为 Object 类中的 hashCode() 方法始终返回的是一个对象的 hash 地址,而这个地址是永远不相等的,即使重写了 equals() 方法,但一开始 hashCode() 方法返回的哈希值都不在一个区域的话那么就不会去调用 equals() 方法了。

上述我们就能得出一个结论:

equals() 相等的两个对象,hashCode() 一定相等;equals() 不相等的两个对象,hashCode() 有可能相等。

5. hashCode 使用注意事项

首先我们看个例子,我们重写了测试类的 equals() 和 hashCode() 方法,get 和 set 方法并没有贴上来:

class ObjectTest {
    private int x;
    private int y;

    @Override
    public boolean equals(Object object) {
        if (object == null) {
        return false;
    }
    if (object instanceof ObjectTest) {
        ObjectTest t = (ObjectTest)object;
            if (t.getX() == this.getX() && t.getY() == this.getY()) {
            return true;
            }
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        final int temp = 11;
        int result = 1;
        result = temp * result + x;
        result = temp * result + y;
        return result;
    }
}

测试:

public static void main(String[] args) {
    HashSet<ObjectTest> set = new HashSet<>();
    ObjectTest o1 = new ObjectTest(2, 2);
    ObjectTest o2 = new ObjectTest(3, 3);
    ObjectTest o3 = new ObjectTest(2, 2);

    set.add(o1);
    set.add(o2);
    set.add(o3);
    set.add(o1);
    System.out.println(set.size());
}

打印结果为:2。这个很好理解,存入了o1、o2;
当我们把 hashCode 的方法注释掉,打印结果为:3。因为Object中的hashCode方法返回的是对象本地内存地址的换算结果,o1与o3虽然equals相同,但是hashCode不同,就不会去验证equals的方法;

C语言-光伏MPPT算法:电导增量法扰动观察法+自动全局搜索Plecs最大功率跟踪算法仿真内容概要:本文档主要介绍了一种基于C语言实现的光伏最大功率点跟踪(MPPT)算法,结合电导增量法与扰动观察法,并引入自动全局搜索策略,利用Plecs仿真工具对算法进行建模与仿真验证。文档重点阐述了两种经典MPPT算法的原理、优缺点及其在不同光照和温度条件下的动态响应特性,同时提出一种改进的复合控制策略以提升系统在复杂环境下的跟踪精度与稳定性。通过仿真结果对比分析,验证了所提方法在快速性和准确性方面的优势,适用于光伏发电系统的高效能量转换控制。; 适合人群:具备一定C语言编程基础和电力电子知识背景,从事光伏系统开发、嵌入式控制或新能源技术研发的工程师及高校研究人员;工作年限1-3年的初级至中级研发人员尤为适合。; 使用场景及目标:①掌握电导增量法与扰动观察法在实际光伏系统中的实现机制与切换逻辑;②学习如何在Plecs中搭建MPPT控制系统仿真模型;③实现自动全局搜索以避免传统算法陷入局部峰值问题,提升复杂工况下的最大功率追踪效率;④为光伏逆变器或太阳能充电控制器的算法开发提供技术参考与实现范例。; 阅读建议:建议读者结合文中提供的C语言算法逻辑与Plecs仿真模型同步学习,重点关注算法判断条件、步长调节策略及仿真参数设置。在理解基本原理的基础上,可通过修改光照强度、温度变化曲线等外部扰动因素,进一步测试算法鲁棒性,并尝试将其移植到实际嵌入式平台进行实验验证。
【无人机协同】动态环境下多无人机系统的协同路径规划与防撞研究(Matlab代码实现)​ 内容概要:本文围绕动态环境下多无人机系统的协同路径规划与防撞问题展开研究,提出基于Matlab的仿真代码实现方案。研究重点在于在复杂、动态环境中实现多无人机之间的高效协同飞行与避障,涵盖路径规划算法的设计与优化,确保无人机集群在执行任务过程中能够实时规避静态障碍物与动态冲突,保障飞行安全性与任务效率。文中结合智能优化算法,构建合理的成本目标函数(如路径长度、飞行高度、威胁规避、转弯角度等),并通过Matlab平台进行算法验证与仿真分析,展示多机协同的可行性与有效性。; 适合人群:具备一定Matlab编程基础,从事无人机控制、路径规划、智能优化算法研究的科研人员及研究生。; 使用场景及目标:①应用于灾害救援、军事侦察、区域巡检等多无人机协同任务场景;②目标是掌握多无人机系统在动态环境下的路径规划与防撞机制,提升协同作业能力与自主决策水平;③通过Matlab仿真深入理解协同算法的实现逻辑与参数调优方法。; 阅读建议:建议结合文中提供的Matlab代码进行实践操作,重点关注目标函数设计、避障策略实现与多机协同逻辑,配合仿真结果分析算法性能,进一步可尝试引入新型智能算法进行优化改进。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值