java谜题-库谜题(复写hashcode与equal方法)

哈希函数解决字谜问题 利用三个哈希值,和矩阵的旋转解决字符串问题,已给定部分字符串,求其出现概率 立即下载

谜题57:名字里有什么?

下面的程序包含了一个简单的不可变类,它表示一个名字,其main方法将一个名字置于一个集合中,并检查该集合是否确实包含了该名字。那么,这个程序到底会打印出什么呢?
import java.util.*;
public class Name {
    private String first, last;
    public Name(String first, String last) {
        this.first = first;
        this.last = last;
    }
    public boolean equals(Object o) {
        if (!(o instanceof Name))
            return false;
        Name n = (Name)o;
        return n.first.equals(first) && n.last.equals(last);
    }
    public static void main(String[] args) {
        Set s = new HashSet();
        s.add(new Name("Mickey", "Mouse"));
        System.out.println(
            s.contains(new Name("Mickey", "Mouse")));
    } 
}

一个Name实例由一个姓和一个名构成。两个Name实例在通过equals方法进行计算时,如果它们的姓相等且名也相等,则这两个Name实例相等。姓和名是用在String中定义的equals方法来比较的,两个字符串如果以相同的顺序包含相同的若干个字符,那么它们就相等。因此,两个Name实例如果表示相同的名字,那么它们就相等。例如,下面的方法调用将返回true:

new Name("Mickey", "Mouse").equals(new Name("Mickey", "Mouse"))

该程序的main方法创建了两个Name实例,它们都表示Mickey Mouse。该程序将第一个实例放置到了一个散列集合中,然后检查该集合是否包含第二个实例。这两个Name实例是相等的,因此看起来该程序似乎应该打印true。如果你运行它,几乎可以肯定它将打印false。那么这个程序出了什么问题呢?

这里的bug在于Name违反了hashCode约定。这看起来有点奇怪,因为Name连hashCode都没有,但是这确实是问题所在。Name类覆写了equals方法,而hashCode约定要求相等的对象要具有相同的散列码。为了遵守这项约定,无论何时,只要你覆写了equals方法,你就必须同时覆写hashCode方法[EJ Item 8]。

因为Name类没有覆写hashCode方法,所以它从Object那里继承了其hashCode实现。这个实现返回的是基于标识的散列码。换句话说,不同的对象几乎总是产生不相等的散列值,即使它们是相等的也是如此。所以说Name没有遵守hashCode的约定,因此包含Name元素的散列集合的行为是不确定的。

当程序将第一个Name实例放置到散列集合中时,该集合就会在某个散列位置上放置这个实例对应的项。该集合是基于实例的散列值来选择散列位置的,这个散列值是通过实例的hashCode方法计算出来的。

当该程序在检查第二个Name实例是否包含在散列集合中时,它基于第二个实例的散列值来选择要搜索的散列位置。因为第二个实例有别于第一个实例,因此它极有可能产生不同的散列值。如果这两个散列值映射到了不同的位置,那么contains方法将返回false:我们所喜爱的啮齿动物米老鼠就在这个散列集合中,但是该集合却找不到他。

假设两个Name实例映射到了相同的位置,那又会怎样呢?我们所了解的所有的HashSet实现都进行了一种优化,即每一项在存储元素本身之外,还存储了元素的散列值。在搜索某个元素时,这种实现通过遍历集合中的项,去拿存储在每一项中的散列值与我们想要查找的元素的散列值进行比较,从而选取适当的散列位置。只有在两个元素的散列值相等的情况下,这种实现才会认为这两个元素相等。这种优化是有实际意义的,因为比较散列码相对于比较元素来说,其代价要小得多。

对散列集合来说,这项优化并不足以使其能够搜索到正确的位置;两个Name实例必须具有相同的散列值才能让散列集合能够将它们识别为是相等的。该程序偶尔也会打印出true,这是因为被连续创建的两个对象偶尔也会具有相同的标识散列码。一个粗略的实验表明,这种偶然性出现的概率大约是25,000,000分之一。这个实验的结果可能会因所使用的Java实现的不同而有所变化,但是在任何我们所知的JRE上,你基本上是不可能看到该程序打印出true的。

要想订正该程序,只需在Name类中添加一个恰当的hashCode方法即可。尽管任何其返回值仅有姓和名来确定的方法都可以满足hashCode的约定,但是高质量的散列函数应该尝试着对不同的名字返回不同的散列值。下面的方法就能够很好地实现这一点[EJ Item 8]。只要我们把该方法添加到了程序中,那么该程序就可以打印出我们所期望的true:

public int hashCode() {
    return 37 * first.hashCode() + last.hashCode();
}

总之,当你覆写equals方法时,一定要记着覆写hashCode方法。更一般地讲,当你在覆写一个方法时,如果它具有一个通用的约定,那么你一定要遵守它。对于大多数在Object中声明的非final的方法,都需要注意这一点[EJ Chapter 3]。不采用这项建议就会导致任意的、不确定的行为。

谜题58:产生它的散列码

本谜题试图从前一个谜题中吸取教训。下面的程序还是由一个Name类和一个main方法构成,这个main方法还是将一个名字放置到一个散列集合中,然后检查该集合是否包含了这个名字。然而,这一次Name类已经覆写了hashCode方法。那么下面的程序将打印出什么呢?
import java.util.*;
public class Name {
    private String first, last;
    public Name(String first, String last) {
        this.first = first; this.last = last;
    }
    public boolean equals(Name n) {
        return n.first.equals(first) && n.last.equals(last);
    }    
    public int hashCode() {
        return 31 * first.hashCode() + last.hashCode(); 
    }
    public static void main(String[ ] args) {
        Set s = new HashSet();
        s.add(new Name("Donald", "Duck"));
        System.out.println(
            s.contains(new Name("Donald", "Duck")));
    }
}

与谜题57一样,该程序的main方法创建了两个Name实例,它们表示的是相同的名字。这一次使用的名字是Donald Duck而不是Mickey Mouse,但是它们不应该有很大的区别。main方法同样还是将第一个实例置于一个散列集合中,然后检查该集合中是否包含了第二个实例。这一次hashCode方法明显是正确的,因此看起来该程序应该打印true。但是,表象再次欺骗了我们:它总是打印出false。这一次又是哪里出错了呢?

这个程序的缺陷与谜题57中的缺陷很相似,在谜题57中,Name覆写了equals方法,但是没有覆写hashCode方法;而在本谜题中,Name覆写了hashCode方法,但是没有覆写equals方法。这并不是说Name没有声明一个equals方法,它确实声明了,但是那是个错误的声明。Name类声明了一个参数类型是Name而不是Object的equals方法。这个类的作者可能想要覆写equals方法,但是却错误地重载了它[JLS 8.4.8.1, 8.4.9]。

HashSet类是使用equals(Object)方法来测试元素的相等性的;Name类中声明一个equals(Name)方法对HashSet不造成任何影响。那么Name是从哪里得到了它的equals(Object)方法的呢?它是从Object哪里继承而来的。这个方法只有在它的参数与在其上调用该方法的对象完全相同时才返回true。我们的程序中的main方法将一个Name实例插入到了散列集合中,并且测试另一个实例是否存在于该散列集合中,由此可知该测试一定是返回false的。对我们而言,两个实例可以代表那令人惊奇的水禽唐老鸭,但是对散列映射表而言,它们只是两个不相等的对象。

订正该程序只需用可以在谜题57中找到的覆写的equals方法来替换重载的equals方法即可。通过使用这个equals方法,该程序就可以打印出我们所期望的true:

public boolean equals(Object o) {
    if (!(o instanceof Name))
        return false;
    Name n = (Name)o;
    return n.first.equals(first) && n.last.equals(last);
}

要让该程序可以正常工作,你只需增加一个覆写的equals方法即可。你不必剔除那个重载的版本,但是你最好是删掉它。重载为错误和混乱提供了机会[EJ Item 26]。如果兼容性要求强制你必须保留一个自身类型的equals方法,那么你应该用自身类型的重载去实现Object的重载,以此来确保它们具有相同的行为:


public boolean equals(Object o) { return o instanceof Name && equals((Name) o); }


本谜题的教训是:当你想要进行覆写时,千万不要进行重载。为了避免无意识地重载,你应该机械地对你想要覆写的每一个超类方法都拷贝其声明,或者更好的方式是让你的IDE帮你去做这些事。这样做除了可以保护你免受无意识的重载之害,而且还可以保护你免受拼错方法名之害。如果你使用的5.0或者更新的版本,那么对于那些意在覆写超类方法的方法,你可以将@Override注释应用于每一个这样的方法的声明上:

@Override public Boolean equals(Object o) { ... }

在使用这个注释时,除非被注释的方法确实覆写了一个超类方法,否则它将不能编译。对语言设计者来说,值得去考虑在每一个覆写超类方法的方法声明上都添加一个强制性的修饰符。

在线考试系统源码+数据库+word+ppt(高分毕业设计) 本系统通过Spring Cloud实现微服务拆分,将授权、考试、监控等功能解耦,既保证了系统的扩展性,也便于后期维护。基于OAuth2.0的单点登录,解决多服务统一认证问题;考试与练习模式分离,满足不同场景需求;集成Druid与Hystrix,保障系统监控与稳定性。 阅读详情

相关推荐

centos8 安装新版bind9源码

bind一直在更新,使用了比较多的新架构 本文记录新版本的bind怎么安装,以及其依赖库的安装 1. 在https://www.isc.org/download/ 下载最新stable版本bind,本文以bind-9.17.9.tar.xz为例。将软件传到/root目录下。 也可以到https://ftp.isc.org/isc/bind9/ 下载更多历史版本 2. 安装基本的依赖库,像gcc、amake等 3. 需要安装libuv库,但直接用yum时可能安装不全 建议使用源码安装,需要安..

linggang_123的博客 2539

【Java基础】重写equals方法详讲

重写equals方法 【Java比较学习】重写equals方法的安全写法 重写equals方法的两种方式 这里提供两个比较常见的equals重写方法: ● 用instanceof实现重写equals方法 ● 用getClass实现重写equals方法 先说结论,getClass()比instanceof更安全。接下来就是我们自己要来实现equals方法了。 场景描述 假设有此场景: 在已经创建好的长方形类中重写Object类中的equals方法为当长方形的长和宽相等时,返回TRUE,同时重写hashCode

qq_43783527的博客 2万+

MATLAB机械臂轨迹规划:在关节空间中生成多项式轨迹以及 jtraj函数(内容超详细讲解)

matlab机器人仿真,关节空间多项式轨迹生成

qq_40969179的博客 1万+

一、哈希问题

LeetCode热题100——哈希问题。

不积跬步无以至千里 1163

Flowable高级篇 -6.8.0 最全表结构说明-主要表结构字段注释

flowable 28张表 详细

billzhen的博客 1万+

FISCO-BCOS配置及使用控制台

如果因为网络问题长时间无法下载,请尝试:curl -#LO https://gitee.com/FISCO-BCOS/console/raw/master-2.0/tools/download_console.sh && bash download_console.sh。为了可以更加方便快捷,所以HelloWorld合约已经被内置了,位于控制台下面的contracts/solidity/HelloWorld.sol。用于获取和设置name的值,合约代码如下。部署成功会返回合约地址。

qq_51562644的博客 831

hashCode()与equals()必须同时复写

hashCode()与equals()必须同时复写, 成对出现,复写equals必须复写hashCode,否则会造成Map、Set等异常。 如通过put覆盖原map中的键值对,但如果只复写了equals,会造成map.gey(existKey)返回为null,从而map里出现多个同样的key,进一步造成内存溢出。因此,必须同时复写hashCode,使map.gey(existKey) -> value 实验如下: static class Cluster { String name

li_mengjun的博客 424

西门子S7-1200控制伺服/步进电机方法与接线(全)

西门子S7-1200控制伺服/步进电机方法与接线(全) 伺服/步进电机在非标自动化控制中十分常用,但作者发现在各类开源网站上很少有人做西门子1200PLC控制伺服/步进电机的教程,于是今天想着跟大家分享一下。 本文共分为一下几个四个内容。 文章目录西门子S7-1200控制伺服/步进电机方法与接线(全)前言一、准备二、接线1.PLC接线:2.伺服接线:3. 步进电机的接线:三、代码组态代码四、可能出现的问题 前言 工程控制常用到三项异步电机、直流电机等,但他们都无法准确的控制电机角度;于是有了步进电机、

weixin_46060068的博客 7万+

如何复写equals() 和 hashCode()

覆写equals方法 1  使用instanceof操作符检查“实参是否为正确的类型”。 2  对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。 3. 对于非float和double类型的原语类型域,使用==比较; 4  对于对象引用域,递归调用equals方法; 5  对于float域,使用Float.floatToIntBits(afloat)转

lifeflowers的神秘世界 1895

Codex 实战 Skills:用 Skill 自动识别并提取 PDF/图片表格数据,效率提升 10 倍

本文介绍如何构建一个多模态文件解析 Skill,用于自动识别和提取 PDF/图片中的表格数据,提升 AI 智能体处理非结构化文档的效率。文章分析了传统方法的局限性,提出分层解析架构(预处理、检测、提取推理、后处理),推荐技术栈包括 pdfplumber、OpenCV 和多模态大模型。详细说明了 Skill 的元数据定义、数据模型设计(使用 Pydantic)和核心实现流程,通过混合策略实现从复杂文档到结构化 JSON/Markdown 的转换,使 AI 代理具备"阅读"多模态文档的能力。

yueyeguzhuo的专栏 302

DMA engine initialization failed rk3568的原因及解决方案

刚开始调试 switch 芯片的时候由于rgmii 时钟是output , rk3568的 GMAC 需要 switch 芯片提供 125M的时钟,如果没有这个时钟就会初始化失败,进入文件系统后 ethx 不会出现(当然,如果屏蔽掉这里ethx 会出现,但是引发更多的问题,笔者在这块耍小聪明,导致耗费了近一个月时间,把网络这块整个查了一遍 -----)dma 软复位是在进入文件系统后,网络系统启动的时候,我想如果在这个之前只要提供合适的时钟那么就是可以的,那么------以上是官方的文档说明。

Nyiragongo的博客 4573

复写hashCode的简单策略 《effective java》读书笔记

1.把某个非零的常数值,保存在整型result中。 2.对于对象中每个关键域f进行如下计算: a.为该域计算整形的散列码: 1.如果是 boolean 类型,返回 (f ? 1:0); 2.如果是byte ,char ,short,int 返回 (int)f; 3.如果是long,返回(int)(f ^ (f >>>32)); 4.如果是float,返回Float....

厚积薄发,持之以恒 439

STM32H750VB与AD5292通讯,写入阻值后,AD5292 的阻值没有变化,如何解决?

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1098

区块链期末复习1.1:密码学哈希函数

即哈希之后的内容可以代表哈希之前的内容。比如一个文件很复杂,我们要判断这个文件有没有损失,只需要对比文件前后的哈希值是否相同。(如果损坏了,根据碰撞阻力,哈希值会改变)

weixin_74447766的博客 1958

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

JAVA中复写equals方法

在JAVA中“==”用于比较两个引用对象的地址是否相同。但是如果我们想比较两个对象的内容是否相同,通常会覆写equals方法。equals方法用来比较两个对象的内容是否相等. package org.lyk.entities; public class Point { private int x; private int y; public...

weixin_30374009的博客 219

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

7.5覆写hashCode()方法

在Map中,key是通过计算得到索引值,再通过索引获取value的值。 那么在Map中key值是如何计算的? Object类中有equals()方法和hashCode()方法,equals()方法是判断两个实例是否相等,而hashCode()是计算hash码。 因此,想正确使用Map,需要覆写两个方法,equals()和hashCode()方法。 hashCode()方法:自己编写的hashCode() int hashCode() { int h = 0; h = 31 *

No_senior_freshman的博客 422

重写hashcode

hashCode() 1. 何为hashcode? hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。 hashCode()定义在 JDK 的 Object 类中,这就意味着 Jav

weixin_44321790的博客 6308

JAVA中重写equals()方法的同时要重写hashcode()方法

bject对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:(1)当obj1.equals(obj2)为true时,obj1

lu18225857116的博客 457

重写equal()时为什么也得重写hashCode()之深度解读equal方法与hashCode方法渊源

今天这篇文章我们打算来深度解读一下equal方法以及其关联方法hashCode(),我们准备从以下几点入手分析: 1.equals()的所属以及内部原理(即Object中equals方法的实现原理) 说起equals方法,我们都知道是超类Object中的一个基本方法,用于检测一个对象是否与另外一个对象相等。而在Object类中这个方法实际上是判断两个对象是否具有相同的引用,

zejian的博客 7万+

谜题库

[quote]   金木水火土,谁的腿长?————火腿肠。   有一坛酒埋在地下过了一千年,结果他变成了什么?----酒精。   有一只猪,它走啊走啊,走到了英国,结果他变成了什么?----Pig.   上课老师抽查背课文,小猪,小狗,小猫都举手了,老师会叫谁?--小狗,因为旺旺仙贝。   蝴蝶,蚂蚁,蜘蛛,蜈蚣,他们一起工作,最后哪一个没有领到酬劳?--蜈蚣,因为无功不...

青龙 299

中途相遇法,哈希技术(和为0的4个值,uva 1152)

题目具有对称性,则可以同时从两边出发来找答案,因为时间复杂度由最大的决定,因此让两边时间复杂度尽量一样可以降低总体的时间复杂度,此题中n^2logn+n^2logn 试了下map,9秒超时,用哈希3.540秒。还是快很多的。 因为map随着数据量的增多,logn的时间复杂度开始拖慢速度。 而哈希大部分时候都是O(1),极端情况下是O(n)。 极端情况一般都不用考虑,除非题目是特殊设

xl2015190026的博客 905

equal重写

我记得在《Effictive Java》这本书中,作者给出了建议,当重写equal时需要重写hashCode。具体原因忘了,这个几下,下次补上。今天主要经冲写equal和hashCode的场景。 使用MyBatis进行数据库的处理,在MyBatis中的sql如下: SELECT <include refid="Base_Column_List" /> ...

helloworlddm的博客 1080

哈希算法leetcode刷题总结

题库1. 字母异位词分组(lc.49):Medium 1. 字母异位词分组(lc.49):Medium 题目链接 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 示例: 输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”] 输出: [ [“ate”,“eat”,“tea”], [“nat”,“tan”], [“bat”] ] 说明: 所有输入均为小写字母。 不考虑答案输出的顺序。 由于互为字母异位词的两个字符串包

贝壳er的博客 377

揭秘区块链的核心技术之「哈希与加密算法 」

大家都知道,区块链的关键技术组成主要为:P2P网络协议、共识机制、密码学技术、账户与存储模型。而这些技术中,又以 密码学与共识机制 这两点为最核心。那么今天我们来详细的聊一聊密码学,看一看密码学技术是如何在区块链中应用的。 首先,我们需知道区块链中用到的密码学算法有哪些?其实就两大类: 哈希算法 非对称加密算法 一、区块链中的哈希算法 哈希算法是区块链中用的最多的...

weixin_30758821的博客 2808

Equals方法复写与对象比较

常用比较两个数值或者字符串两种方法==   equals方法有什么区别: equals是判断两个变量或实例所指向的内存空间的值是不是相同 而==是判断两个变量或实例是不是指向同一个内存空间即== 判断是你比较值的地址   而equals比较的是你两个数值是否相等。举例: public class Compare {       public static void main(String[] ar...

AiWQSunny的博客 522

java谜题--java运行时修改引用类的静态常量

谜题93:类的战争 下面这个谜题测试了你关于二进制兼容性(binary compatibility)的知识:当你改变了某个类所依赖的另外一个类时,第一个类的行为会发生什么改变呢?更特殊的是,假设你编译的是如下的2个类。第一个作为一个客户端,第二个作为一个库类,会怎么样呢: public class PrintWords { public static void main(Strin

想飞的鱼 2352
上一篇: BigDecimal 精确计算
下一篇: java谜题--类谜题(父类的static方法)
feiyu8607
博客等级 码龄18年 26粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值