当前位置:编程学习 > JAVA >>

有关对象hashCode可变的疑问

最近,在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<BeanPK,Bean> map = new HashMap<BeanPK,Bean>();
  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方法? --------------------编程问答-------------------- 我刚刚大二 我说说我对于hashcode的比较浅显的理解。我前段时间也有同样的疑问。后来我查了查。
set是要求无序不重复的 而要保证添加进去的每个对象都是不重复的那么只能重写该对象的equals和hashcode方法。在添加的时候就已经改变了hashcode。

也许文不对题 但我就是这样去记住使用的 期待大神的解答。 --------------------编程问答-------------------- 大二就这么厉害了。。。。。

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

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

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

那么 在JPA 文档中对于联合主键@EmbeddedId 大多都说要 重写 hashcode。
那么作为 实体识别 的 @EmbeddedId 能否做key呢!? --------------------编程问答--------------------
引用 2 楼 mingzidaodiduochang 的回复:
大二就这么厉害了。。。。。

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

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

……



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


常规设计的时候,当页面需要批量添加Bean实体时
Action 通常 会传递一个 List<Bean> 或者 Map<BeanPK,Bean> 进入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方法??? --------------------编程问答--------------------
引用 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<Bean> 或者 Map<BeanPK,Bean> 进入Service层
但在Action层中 BeanPK中的 id 还没生成,可能需要到Service层或者Dao层时才分配id”

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

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

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

换句话说,如果BeanPK中的id不满足以上条件,那id不应该用作equals和hashCode的依据。 --------------------编程问答--------------------
引用 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就不行咯!  --------------------编程问答--------------------
引用 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 不发生变化就行了。
补充:Java ,  Java SE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,