热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

有关对象hashCode可变的疑问

最近,在hibernate中使用JPA注解方式配置bean遇到联合主键时采用@EmbeddedIdprivateBeanPKid;再顶一个BeanPK对象@Embedd
最近,在hibernate中使用JPA注解方式配置bean
遇到联合主键时采用
@EmbeddedId
private BeanPK id;

再顶一个 BeanPK 对象
@Embeddable
public class BeanPK implements Serializable{}

看文档中,要求@Embeddable的BeanPK需要重写hashCode、equals方法;
equals好说;
但hashCode问题就来了
就我所知 java中的hashCode是根据对象的内存地址计算得到的;
String特殊一些,根据字符串内容计算得到的;而String常出现在常量池中。
这样java可以保证每个对象的hashCode是一个固定值。

而我重写BeanPK中的hashCode方法,只能根据BeanPK中的属性的hashCode计算而出。
这样一旦BeanPK中的某个属性改变(例如:调用BeanPK.setXXXX()方法)后 hashCode的值就会改变。

这样就会带来几个问题:
1.当BeanPK放入到集合中
  由于对象存放到集合中的位置依赖对象的hashCode值。
  那么,当对象放入集合后,再改变hashCode的值,集合就找不到该对象了。
  Set中也就无法过滤重复对象。
例子:
  BeanPK pk = new BeanPK("id","name");
  Bean b = new Bean(pk,"age");
  Map map = new HashMap();
  map.put(pk,b);
  pk.setName("newName");//BeanPK 的hashCode会根据id和name计算,setName后hashCode改变
  map.get(pk);//由于hashCode改变,Map已经找不到匹配的key了,这里返回null


2.由于@Embeddable要求实现Serializable
  那么当反序列后,hashCode不一致会不会带来同样的问题?

希望高手指定一下,
另外:在使用@Embeddable时,需不需要重写hashCode方法?

8 个解决方案

#1


我刚刚大二 我说说我对于hashcode的比较浅显的理解。我前段时间也有同样的疑问。后来我查了查。
set是要求无序不重复的 而要保证添加进去的每个对象都是不重复的那么只能重写该对象的equals和hashcode方法。在添加的时候就已经改变了hashcode。

也许文不对题 但我就是这样去记住使用的 期待大神的解答。

#2


大二就这么厉害了。。。。。

hashCode不一定是根据内存得到的。string对象的hashCode,就是每个字符乘以31的次方作为分别的权重计算得到的。你可以看下源码。实际上hash函数的设计本来就很复杂。像你这种情况要避免变动的成员用来计算hashCode。

不过内容变了对象本来就不是同一个了吧。。比如你这个对象,性别变成女了。。还是你自己么。。

#3


Map的key要求是一个不可变的值对象。可变的不应该作为key。

#4


引用 3 楼 huntor 的回复:
Map的key要求是一个不可变的值对象。可变的不应该作为key。

那么 在JPA 文档中对于联合主键@EmbeddedId 大多都说要 重写 hashcode。
那么作为 实体识别 的 @EmbeddedId 能否做key呢!?

#5


引用 2 楼 mingzidaodiduochang 的回复:
大二就这么厉害了。。。。。

hashCode不一定是根据内存得到的。string对象的hashCode,就是每个字符乘以31的次方作为分别的权重计算得到的。你可以看下源码。实际上hash函数的设计本来就很复杂。像你这种情况要避免变动的成员用来计算hashCode。

不过内容变了对象本来就不是同一个了吧。。比如你这个对象,性别变成女了。。还是你自己么。。

……



就我所知 java中的hashCode是根据对象的内存地址计算得到的;
String特殊一些,根据字符串内容计算得到的;而String常出现在常量池中。


常规设计的时候,当页面需要批量添加Bean实体时
Action 通常 会传递一个 List 或者 Map 进入Service层
但在Action层中 BeanPK中的 id 还没生成,可能需要到Service层或者Dao层时才分配id

那么在设计的时候 是否应该在 Action 和 Service 以及Dao 层中传递不同的集合?
(也就是说 从Action 开始传递的那个集合 到Service 或者 Dao 层 可能由于BeanPK的hashcode改变 而造成 get() 是一个null)
在设计中如何避免此类问题,
我的印象中 除了 String 以及 基本类型的包装类 以外 基本上都是使用 Object的hashcode方法
而 Object的hashcode 是使用 JNI 方式 直接使用内存地址 计算得出的hashcode

既然如此,是否可在设计时 @EmbeddedId 的对象 不重写hashcode方法,只重写equals方法???

#6


引用 5 楼 qiuyufeifei 的回复:
引用 2 楼 mingzidaodiduochang 的回复:大二就这么厉害了。。。。。

hashCode不一定是根据内存得到的。string对象的hashCode,就是每个字符乘以31的次方作为分别的权重计算得到的。你可以看下源码。实际上hash函数的设计本来就很复杂。像你这种情况要避免变动的成员用来计算hashCode。

不过内容变了对象本来就不是同一个了……


String 是immutable的类,所以不存在你说的问题,另外 String 的 hashCode 实现比较特殊,可以叫“lazy hashcode”,不建议模仿。

"既然如此,是否可在设计时 @EmbeddedId 的对象 不重写hashcode方法,只重写equals方法??? "
那么你的equals方法又要依据什么来判断呢?
只重写equals方法通常是不对的。因为可能出现两个对象 equals 为 true,而hashCode却不相等的情况,这与HashMap和HashSet是一定不兼容的。

“Action 通常 会传递一个 List 或者 Map 进入Service层
但在Action层中 BeanPK中的 id 还没生成,可能需要到Service层或者Dao层时才分配id”

这句话听起来就是有问题的,"BeanPK中的 id 还没生成",如果BeanPK是依赖id来比较equals,那id应该满足以下条件:

1 - 在构造方法中就已经安全的赋值
2 - 在对象构造以后不能再改变

简单的说,就是 final + immutable(或primitive)
用于hashCode的成员也一样要满足以上条件。

换句话说,如果BeanPK中的id不满足以上条件,那id不应该用作equals和hashCode的依据。

#7


引用 6 楼 raistlic 的回复:
引用 5 楼 qiuyufeifei 的回复:引用 2 楼 mingzidaodiduochang 的回复:大二就这么厉害了。。。。。

hashCode不一定是根据内存得到的。string对象的hashCode,就是每个字符乘以31的次方作为分别的权重计算得到的。你可以看下源码。实际上hash函数的设计本来就很复杂。像你这种情况要避免变动的成员用来计算hashCod……


看来 BeanPK中的属性 需要使用 final 才能避免一些问题。
但这里还有一个问题,
由于 我使用hibernate,当hibernate从数据库中取出对象后,貌似实例对象时使用的是对象的无参构造器,然后调用 set方法
那么 hibernate 会不会调用 BeanPK() 然后再 setId(String id) 呢!!???
如果hibernate按照上述步骤实例,那么 id 设置成final就不行咯! 

#8


引用 7 楼 qiuyufeifei 的回复:
引用 6 楼 raistlic 的回复:引用 5 楼 qiuyufeifei 的回复:引用 2 楼 mingzidaodiduochang 的回复:大二就这么厉害了。。。。。

hashCode不一定是根据内存得到的。string对象的hashCode,就是每个字符乘以31的次方作为分别的权重计算得到的。你可以看下源码。实际上hash函数的设计本来就很复杂。像你这种情……


上面说的情况比较严格,说的是安全的 hash key 应该满足的,——按这个标准所有的 JavaBean 大概都不满足条件(或者只能用默认的 equals 和 hashCode)……

——事实上只要保证:

当一个对象作为 key 被加进 HashMap 或者 HashSet 以后,使用 HashMap 和 HashSet 期间,这个对象的 equals 和 hashCode 不发生变化就行了。

推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • 数字账号安全与数据资产问题的研究及解决方案
    本文研究了数字账号安全与数据资产问题,并提出了解决方案。近期,大量QQ账号被盗事件引起了广泛关注。欺诈者对数字账号的价值认识超过了账号主人,因此他们不断攻击和盗用账号。然而,平台和账号主人对账号安全问题的态度不正确,只有用户自身意识到问题的严重性并采取行动,才能推动平台优先解决这些问题。本文旨在提醒用户关注账号安全,并呼吁平台承担起更多的责任。令牌云团队对此进行了长期深入的研究,并提出了相应的解决方案。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了将mysql从5.6.15升级到5.7.15的详细步骤,包括关闭访问、备份旧库、备份权限、配置文件备份、关闭旧数据库、安装二进制、替换配置文件以及启动新数据库等操作。 ... [详细]
author-avatar
再见看淡_266
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有