软件构造 | Equality in ADT and OOP

news2025/2/25 14:03:08

软件构造 | Equality in ADT and OOP

🧇1 Three ways to regard equality

1.1 Using AF to define the equality

ADT是对数据的抽象, 体现为一组对数据的操作

抽象函数AF:内部表示→抽象表示

基于抽象函数AF定义ADT的等价操作,如果AF映射到同样的结果,则等价

1.2 Using observation to define the equality

​ 站在外部观察者角度:对两个对象调用任何相同的操作,都会得到相同的结果,则认为这 两个对象是等价的。

在这里插入图片描述

1.3 == vs. equals()

== 引用等价性 : 对基本数据类型,使用==判定相等

equals() 对象等价性 : 对对象类型,使用equals()

在自定义ADT时,需要根据对“等价”的要求, 决定是否重写Object的equals()

1.4 Implementing equals()

The equals() method is defined by Object , and its default implementation looks like this:

在这里插入图片描述

Note:在Object中实现的默认equals()是在判断引用等价性,这通常不是程序员所期望的,因此,需要重写。

public classDuration {
    ...
    // Problematic definition of equals()
    public boolean equals(Duration that) {
		return this.getLength() == that.getLength();
    }
}

错误声明equal:实现的是重载而不是重写。

在这里插入图片描述

正确声明

在这里插入图片描述

在 Java 中,equals() 方法是用于比较两个对象是否相等的方法。在 Java 中,所有的类都继承自 Object 类,而 Object 类中的 equals() 方法默认实现是比较两个对象的引用是否相同(即比较内存地址)。因此,当需要在自定义类中比较对象内容时,通常需要重写 equals() 方法。

下面是一个示例,展示了如何重写 equals() 方法来比较自定义类中的对象内容:

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true; // 如果是同一个对象,直接返回true
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false; // 如果对象为null或者是不同类的实例,直接返回false
        }
        Person person = (Person) obj; // 强制转换为Person类
        return age == person.age && name.equals(person.name); // 比较name和age是否相等
    }

    // 省略 getter 和 setter 方法
}

public class Example {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 30);
        Person person2 = new Person("Alice", 30);

        System.out.println(person1.equals(person2)); // 输出true,因为内容相同
    }
}

在上面的示例中,Person 类重写了 equals() 方法,根据对象的 nameage 属性来比较两个 Person 对象是否相等。在 equals() 方法中,首先比较对象引用是否相同,然后再比较类和属性是否相同。

需要注意的是,在重写 equals() 方法时,通常需要同时重写 hashCode() 方法,以确保两个方法的一致性。这样可以保证对象在放入基于散列的集合(如 HashMapHashSet)时能够正确地根据对象内容对其进行存储和检索。

🍕 2The Object contract(对象合同)

  1. 等价关系:自反、传递、对称。
  2. 除非对象被修改了,否则调用多次equals应是同样的结果。
  3. “相等”的对象,其hashCode()的结果必须一 致。

Note:用“是否为等价关系”检验你的equals()是否正确

2.1 Hash Tables

在这里插入图片描述

Object ’s default hashCode() implementation is consistent with its default equals()

在这里插入图片描述

键值对中的 key被映射为hashcode,对应到数组的index, hashcode决定了数据被存 储到数组的哪个位置

2.2 Overriding hashCode()

在 Java 中,每个对象都有一个 hashCode,它是对象的哈希码,用于确定对象在哈希表等数据结构中的存储位置。hashCode 的作用是为了更高效地进行对象的存储和检索,特别是在使用基于散列的集合(如 HashMapHashSet)时非常重要。

hashCode 方法的设计要求是:

  1. 如果两个对象通过 equals() 方法相等,则它们的 hashCode 必须相等。
  2. 如果两个对象的 hashCode 相等,它们并不一定通过 equals() 方法相等。

在实现类中重写 hashCode 方法时,通常需要保证满足上述两个要求。通常情况下,可以利用对象的属性来生成 hashCode 值,这样可以确保同样属性的对象具有相同的 hashCode

最简单方法:让所有对象的hashCode为同一 常量,符合contract,但降低了hashTable效率

通过 equals计算中用到的所有信息的hashCode组合出新的hashCode

在 Java 中,当两个对象通过 equals() 方法相等且具有相同的 hashCode 值时,如果将这两个对象放入基于散列的集合(如 HashMapHashSet)中,只会存储一个对象。这是因为散列集合在存储对象时会先根据 hashCode 值确定对象在内部数据结构中的存储位置,然后再通过 equals() 方法来判断具体位置是否已经存在相同的对象。

具体来说,当向散列集合中添加一个对象时,首先会计算该对象的 hashCode 值,然后根据 hashCode 值找到对象在内部存储结构中的位置。如果在该位置处已经有一个对象存在,并且这个对象与新添加的对象通过 equals() 方法比较相等(即返回 true),那么新添加的对象不会被存储,以保证集合中不会存在重复的对象。

下面是一个简单示例,演示了两个相等的对象具有相同 hashCode 值时,只存储一个对象的情况:

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

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + name.hashCode();
        result = 31 * result + age;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Person)) {
            return false;
        }
        Person other = (Person) obj;
        return this.name.equals(other.name) && this.age == other.age;
    }
}

public class Example {
    public static void main(String[] args) {
        Map<Person, String> personMap = new HashMap<>();

        Person person1 = new Person("Alice", 30);
        Person person2 = new Person("Alice", 30);

        personMap.put(person1, "Value 1");
        personMap.put(person2, "Value 2");

        System.out.println(personMap.size()); // 输出 1,因为两个对象相等且具有相同的 hashCode
    }
}

在上面的示例中,Person 类重写了 equals()hashCode() 方法,确保在两个对象具有相同属性值时返回 true 并且具有相同的哈希码。当将这两个相等的对象放入 HashMap 中时,只会存储一个对象,因为它们具有相同的 hashCode 值。因此,最终输出的大小是 1

Always override hashCode() when you override equals()

除非你能保证你的ADT不会被放入到Hash类型的集合类中

🍿 3引用的概念

在 Java 中,引用是指向对象的指针或句柄。在 Java 中,所有对象都是通过引用来操作的,而不是直接访问对象本身。当您创建一个对象时,实际上是在堆内存中为该对象分配了空间,并返回一个引用,这个引用指向堆中的对象。Java 的引用是一种高级抽象概念,开发人员无法直接控制对象所在的内存位置,只能通过引用去访问和操作对象。

与此相对应,C 语言中的指针是直接指向内存地址的变量。在 C 中,通过指针可以直接访问或修改内存地址中的数据。指针在 C 语言中被广泛用于实现动态内存分配、访问数组元素、操作数据结构等。

下面是一个简单的示例来对比 Java 中的引用和 C 中的指针:

在 Java 中:

public class Example {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = str1;
        
        System.out.println(str1); // Hello
        System.out.println(str2); // Hello
        
        str2 = "World";
        
        System.out.println(str1); // Hello
        System.out.println(str2); // World
    }
}

在这个示例中,str1str2 都是对象的引用,它们最初都指向同一个字符串对象"Hello"。当我们修改 str2 的值时,它指向了一个新的字符串对象"World",但str1 仍然指向原来的字符串对象"Hello"。

在 C 中:

#include <stdio.h>

int main() {
    int var = 10;
    int* ptr = &var;
    
    printf("Original value: %d\n", var); // 10
    printf("Value through pointer: %d\n", *ptr); // 10
    
    *ptr = 20;
    
    printf("Updated value: %d\n", var); // 20
    printf("Value through pointer: %d\n", *ptr); // 20
    
    return 0;
}

在这个示例中,ptr 是一个指向 var 变量的指针。通过 *ptr 可以访问或修改 var 的值。在这段代码中,我们通过指针修改了 var 的值,而不是通过变量名 var 直接修改。

综上所述,Java 中的引用是指向对象的抽象概念,开发者无法直接操作对象的内存地址,只能通过引用访问对象;而 C 中的指针直接指向内存地址,开发者可以直接控制和操作内存地址中的数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1841947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL----事务的隔离级别(附带每一级别实例截图)

先来回顾一下事务并发可能存在的三大问题&#xff1a; 脏读&#xff08;Dirty Read&#xff09;–不能接受 一个事务读取了另一个事务未提交的数据。例如当事务A和事务B并发执行时&#xff0c;当事务A更新后&#xff0c;事务B查询读取到A尚未提交的数据&#xff0c;此时事务A…

探索客户端-服务器架构:网络应用和分布式系统的基石

目录 前言1 客户端-服务器架构概述1.1 客户端的角色1.2 服务器的角色 2 客户端-服务器架构的工作原理3 客户端-服务器架构的应用4 客户端-服务器架构的优缺点4.1 优点方面4.2 缺点方面 5 客户端-服务器架构的未来发展结语 前言 在当今信息技术飞速发展的时代&#xff0c;客户端…

【数据结构】第十八弹---C语言实现堆排序

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、堆排序 1.1、基本思想 1.2、初步代码实现 1.3、代码优化 1.4、代码测试 总结 1、堆排序 在博主数据结构第十二弹---堆的应用有详细讲解堆…

【SpringMVC】第1-7章

第1章 初始SpringMVC 1.1 学习本套教程前的知识储备 JavaSEHTMLCSSJavaScriptVueAJAX axiosThymeleafServletMavenSpring 1.2 什么是MVC MVC架构模式相关课程&#xff0c;在老杜的JavaWeb课程中已经详细的讲解了&#xff0c;如果没有学过的&#xff0c;可以看这个视频&…

kafka学习笔记07

Kafka高可用集群搭建节点需求规划 开放端口。 Kafka高可用集群之zookeeper集群搭建环境准备 删除之前的kafka和zookeeper。 重新进行环境部署&#xff1a; 我们解压我们的zookeeper: 编辑第一个zookeeper的配置文件: 我们重复类似的操作&#xff0c;创建三个zookeeper节点: 记…

最新暑假带刷规划:50天吃透660+880!

现在只刷一本题集根本不够 去做做24年的考研真题卷就什么都明白了&#xff0c;24年的卷子就是典型的知识点多&#xff0c;杂&#xff0c;计算量大。 而现在市面上的任何一本题集&#xff0c;都无法做到包含所有的知识点&#xff0c;毕竟版面有限&#xff01; 所以&#xff0…

Python+Pytest+Yaml+Allure接口自动化测试框架详解

PythonPytestYamlAllure整体框架目录&#xff08;源代码请等下篇&#xff09; 框架详解 common:公共方法包 –get_path.py:获取文件路径方法 –logger_util.py:输出日志方法 –parameters_until.py&#xff1a;传参方式方法封装 –requests_util.py&#xff1a;请求方式方法封…

《人工智能导论》书面作业

第 1 章&#xff1a;绪论 1、分别解释人工智能的三个主要学派的代表人物和主要思想&#xff0c;并给出每个学派的一个实际应用实例。 符号主义&#xff08;Symbolists 或 逻辑主义&#xff09;&#xff1a; 代表人物&#xff1a;马文闵斯基&#xff08;Marvin Minsky&#xf…

SEO工具,SEO优化人员必备工具

工欲善其事必先利其器&#xff0c;现在是一个讲究效率的时代&#xff0c;学会使用工具&#xff0c;往往能事半功倍&#xff01;使用SEO工具可以帮助你更有效地进行关键词研究&#xff0c;创建高质量的内容&#xff0c;以及建立高质量的外部链接。通过这些工具&#xff0c;你可以…

全面赋能,永久免费!讯飞星火API能力正式免费开放

2023年5月&#xff0c;讯飞星火正式发布&#xff0c;迅速成为千万用户获取知识、学习知识的“超级助手”&#xff0c;成为解放生产力、释放想象力的“超级杠杆”。 2024年5月&#xff0c;讯飞星火API能力正式免费开放&#xff0c;携手生态开发者加快大模型赋能刚需场景。 领…

嵌入式开发十九:SysTick—系统定时器

在前面实验中我们使用到的延时都是通过SysTick进行延时的。 我们知道&#xff0c;延时有两种方式&#xff1a;软件延时&#xff0c;即CPU 循环等待产生的&#xff0c;这个延时是不精确的。第二种就是滴答定时器延时&#xff0c;本篇博客就来介绍 STM32F4 内部 SysTick 系统定时…

如何手撸一个自有知识库的RAG系统

RAG通常指的是"Retrieval-Augmented Generation"&#xff0c;即“检索增强的生成”。这是一种结合了检索&#xff08;Retrieval&#xff09;和生成&#xff08;Generation&#xff09;的机器学习模型&#xff0c;通常用于自然语言处理任务&#xff0c;如文本生成、问…

硬件开发笔记(二十):AD21导入外部下载的元器件原理图库、封装库和3D模型

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139707771 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

一文读懂分布式系统CAP理论与BASE理论概念

CAP理论 Cap理论又被称作布鲁尔定理(Brewers theorem),它指出对于一个分布式系统来说,不可能同时满足如下三点: 一致性(Consistency) 可用性(Availability) 分区容错性(Partition tolerance)(以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达…

windows 系统根据端口查找进程,杀死进程

在启动项目时&#xff0c;往往设置的端口被占用&#xff0c;这时需要杀死端口所占用的进程&#xff0c;然后再重启项目。 netstat -ano | findstr :8085 taskkill /F /PID 25184 杀死进程后&#xff0c;再执行命令 netstat -ano | findstr :8085 进行查看端口占用情况

B树(数据结构篇)

数据结构之B树 B-树(B-tree) 概念&#xff1a; B-树是一个非二叉树的多路平衡查找树(数据有序)&#xff0c;是一颗所有数据都存储在树叶节点上的树&#xff0c;不一定存储具体的数据&#xff0c;也可以是指向包含数据的记录的指针或地址 对于**阶为M(子节点数量在2和M之间)*…

嵌入式数据库_2.嵌入式数据库的一般架构

嵌入式数据库的架构与应用对象紧密相关&#xff0c;其架构是以内存、文件和网络等三种方式为主。 1.基于内存的数据库系统 基于内存的数据库系统中比较典型的产品是每个McObject公司的eXtremeDB嵌入式数据库&#xff0c;2013年3月推出5.0版&#xff0c;它采用内存数据结构&…

Ansys Mechanical|学习方法

Ansys Mechanical是Ansys的旗舰产品之一&#xff0c;涉及的学科体系全面丰富&#xff0c;包括的力学分支主要有理论力学&#xff0c;振动理论&#xff0c;连续介质力学&#xff0c;固态力学&#xff0c;物理力学&#xff0c;爆炸力学及应用力学等。 在自媒体及数字经济飞速发展…

Samtec制造理念系列一 | 差异变量的概念

【摘要/前言】 制造高端电子产品是非常复杂精密的过程。制作用于演示或原型的一次性样品可能具有挑战性&#xff0c;但真正的挑战在于如何以盈利的方式持续生产。 这就是Samtec风险投资研发工程总监Aaron Tucker在一次关于生产高密度微小型连接器的挑战的演讲中所强调的观点。…

使用QMainWindow、QMenuBar,QToolBar文本编辑器界面布局设置

使用QMainWindow、QMenuBar&#xff0c;QToolBar设计一个文本编辑器的界面 菜单 菜单输入处输入 文件$F ,呈现文件(F),快捷键AltF ,打开文件菜单 添加工具栏 在窗体空白处&#xff0c;右键添加工具栏 Action工具 在Designer界面下方 批量定义action 拖入到menu和 toolBar中 Too…