java hashcode相等_关于java:hashCode实现,用于“等于某些字段相等”
我希望特定类的对象如果它们的字段之一相等则相等。如何为此类编写一致的hashCode方法?
(免责声明,因为我知道这不是最佳实践:该类是另一个类的包装器,应该用于Maps中的键。这是为了使这些对象中的两个不同,具有相同的字段,将导致相同的Map条目。实际上每个字段都可以自己标识基础对象,但是我对两个可用对象始终没有相同的标识字段。我无法控制并更改这种"模棱两可"的标识机制。也欢迎您解决此问题的替代解决方案。)
有相应的实现hashCode()的策略吗?我只知道涉及等于中的连词(与&&一样)的实现。如果两个字段相等,如何确保hashCode()相同?
这是简化的equals方法,我想为其编写一致的hashCode()实现:
public boolean equals(C other)
{
return (field1 != null && field1.equals(other.field1))
|| (field2 != null && field2.equals(other.field2))
|| (field3 != null && field3.equals(other.field3));
}
编辑:根据输入数据,不会发生像(1、2、3)等于(1、6、7)的情况。对象的产生仅使某些字段可以为空,但并不与示例中的矛盾。简单地说,等于(1、2、3)的唯一组合应该是(1、2、3),(1,null,null),(null,2,null),(1、2,null)和等等。我承认这种方法并不是特别可靠。
因此,仅在您的哈希码实现中使用该字段。
平等关系不是传递性的。您还是要违约。
有多个字段,并且如果两个实例中的任何一个(成对)相等,则这两个实例被视为"相等"
有几种方法可以执行此操作,但是您应该查看合同以覆盖equals()和hashCode()方法的实现。
问题是(1、2、3、4)和(3、4、5、6)比较等于(3、4、5、6)和(5、6、7、8),但是( 1、2、3、4)和(5、6、7、8)比较不相等。
我上面没有提到的是a.field1.equals(b.field1)&&!a.field2.equals(b.field2)(或类似的组合)不会发生(根据处理后的输入数据)的情况,这又不等于平等传递吗? -我知道这有点骇人听闻,但到目前为止,我还没有找到更干净的解决方案
为什么根本需要这个奇怪的equals方法?不能定义一个实现此逻辑的功能对象,并且只能在实际需要它的孤立地方显式使用它。这将使您的代码更加整洁,并避免将来出现许多令人困惑的错误。
@ 5gon12eder这是我最初想要做的,但是我想将其与HashMap一起使用,并且我无法提供自定义的equals-Implementation。由于类型没有有用的排序,因此我尝试使用自定义Comparator来避免TreeSet
问题中equals()的实现
public boolean equals(C other) {
//TODO: you have to check if other != null...
return (field1 != null && field1.equals(other.field1)) ||
(field2 != null && field2.equals(other.field2)) ||
(field3 != null && field3.equals(other.field3));
}
是不正确的。在实现平等时,我们必须确保
a.equals(a)
if a.equals(b) then b.equals(a)
if a.equals(b) and b.equals(c) then a.equals(c)
第一条规则的反例是所有字段都在其中的实例
比较(field1, field2, field3)为null:
MyObject a = new MyObject();
a.setField1(null);
a.setField2(null);
a.setField3(null);
a.equals(a); //
3d规则的反例:
MyObject a = new MyObject();
a.setField1("1"); // field2 and field3 == null
MyObject b = new MyObject();
b.setField1("1"); // field3 == null
b.setField2("2");
MyObject c = new MyObject();
c.setField2("2"); // field1 and field3 == null
a.equals(b); // true (by field1)
b.equals(c); // true (by field2)
a.equals(c); // false!
这就是为什么hashCode()没有解决方案的原因...
您是正确的,但是这种情况在实践中不会发生,因为如何从输入数据创建对象(不幸的是,我不允许详细说明用例,我可能会在以后尝试澄清问题,但似乎我宁愿一起寻找更好的方法)
@JayK:您的实现也违反了3d规则(请参阅我的编辑)
您是对的,我还没有考虑过这种情况。如果我要回到这种方法,我将不得不评估它与实际输入和周围算法的关系,并在必要时考虑适当的"修复"。
似乎唯一的解决方案是这样
public int hashCode() {
return 1;
}
实际上,我可以想到的还有2 ^ 32个解决方案…第(2 ^ 32 + 1)个解决方案引发了异常。
@Prashant-这是正确的解决方案。 equality合同本身有缺陷,不要指望hashCode()没有缺陷。
确实可以,但是它也会使HashMaps退化,从而降低性能,是吗?
如果您将equals实现为不是等价关系,则Java集合框架仍然会失败。
我得到的@TheLostMind:D
@JayK-HashMap中有一个补充的hash()方法用于较差的hashCodes:P
您不能使用任何字段来实现hashCode,因为该字段并非总是相等。
您的hashCode方法需要始终为equals对象返回相同的值。由于equals方法中只有一个字段需要相等,并且并不总是相同,因此,您唯一的选择是在hashCode方法中返回常量。
该实现效率低下,但有效且与equals一致。
实现可以是:
public int hashCode() {
return 0;
}
OP可能会询问"仅一个字段"是否可用于实现equals()和hashCode()。
@ ha9u63ar-在这种情况下,答案是否定的。 Equals()可以在field3上返回true,但是hasCode()可能基于field1生成。
@ ha9u63ar谢谢,我误解了这个问题,我更新了答案。
通常,您不会仅使用对象类的一个字段来实现equals()和hashCode()。每个人都可能建议您不要这样做。通常的做法是确保您比较所有字段并确保它们相等,以便调用.equals()。 hashCode()使用.equals()来哈希这些对象。但是,如果您可以控制自己的工作,则可以简单地使用对象的特定字段的hashCode()并基于此覆盖.equals()和.hashCode()(但再次建议不要这样做)。
Alternative solutions to tackle this are also welcome.
我永远不会把equals()和hashCode()弄混。只需为对象正确实现它们即可。编写满足您需要的自定义比较器,并使用支持自定义比较器的集合(例如TreeSet或TreeMap)来执行查找。
样本比较器:
public class SingleFieldMatchComparator implements Comparator {
public int compare(Key key1, Key key2) {
if (key1 == null) {
if (key2 == null) {
return 0;
}
else {
return 1;
}
} else if (key2 == null) {
return -1;
}
int result = ObjectUtils.compare(key1.getField1(), key2.getField1());
if (result == 0) {
return 0;
}
result = ObjectUtils.compare(key1.getField2(), key2.getField2());
if (result == 0) {
return 0;
}
return ObjectUtils.compare(key1.getField3(), key2.getField3());
}
}
注意:以上使用ObjectUtils减少了一些代码。如果不值得依赖,可以用私有方法代替。
示例程序:
Map myMap = new TreeMap(new SingleFieldMatchComparator());
Key key = new Key(1, 2, 3);
myMap.put(key, key);
key = new Key(3, 1, 2);
myMap.put(key, key);
System.out.println(myMap.get(new Key(null, null, null)));
System.out.println(myMap.get(new Key(1, null, null)));
System.out.println(myMap.get(new Key(null, 2, null)));
System.out.println(myMap.get(new Key(null, null, 2)));
System.out.println(myMap.get(new Key(2, null, null)));
输出:
null
1,2,3
1,2,3
3,1,2
null
从您所说的不好的做法开始,我认为您可以实现此目的的一种方法是维护对每个对象中另一个对象的引用,并根据字段的相等性计算hashCode:
public class Test {
private String field1;
private Integer field2;
private Test other;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public int getField2() {
return field2;
}
public void setField2(int field2) {
this.field2 = field2;
}
public Test getOther() {
return other;
}
public void setOther(Test other) {
this.other = other;
}
@Override
public int hashCode() {
if (other == null) {
return super.hashCode();
}
int hashCode = 1;
if (field1 != null && field1.equals(other.field1)) {
hashCode = 31 * hashCode + (field1 == null ? 0 : field1.hashCode());
}
if (field2 != null && field2.equals(other.field2)) {
hashCode = 31 * hashCode + field2.hashCode();
}
if (hashCode == 1) {
hashCode = super.hashCode();
}
return hashCode;
}
public boolean equals(Test other) {
return (field1 != null && field1.equals(other.field1))
|| (field2 != null && field2.equals(other.field2));
}
public static void main(String[] args) {
Test t1 = new Test();
t1.setField1("a");
t1.setField2(1);
Test t2 = new Test();
t2.setField1("a");
t2.setField2(1);
t1.setOther(t2);
t2.setOther(t1);
System.out.println("Equals:" + t1.equals(t2));
System.out.println("Hash 1:" + t1.hashCode());
System.out.println("Hash 2:" + t2.hashCode());
t2.setField2(2);
System.out.println("Equals:" + t1.equals(t2));
System.out.println("Hash 1:" + t1.hashCode());
System.out.println("Hash 2:" + t2.hashCode());
t2.setField1("b");
System.out.println("Equals:" + t1.equals(t2));
System.out.println("Hash 1:" + t1.hashCode());
System.out.println("Hash 2:" + t2.hashCode());
}
}
java hashcode相等_关于java:hashCode实现,用于“等于某些字段相等”相关推荐
- java延迟覆盖_高效Java第九条覆盖equals时总要覆盖hashCode
原标题:高效Java第九条覆盖equals时总要覆盖hashCode 高效Java第九条覆盖equals时总要覆盖hashCode 在每个覆盖了equals方法的类中,也必须覆盖hashCode方法. ...
- java高级教程_高级Java教程
java高级教程 课程大纲 学习Java基础很容易. 但是,真正钻研该语言并研究其更高级的概念和细微差别将使您成为一名出色的Java开发人员. 网络上充斥着"软","便宜 ...
- java虚拟机编译_[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式...
前言简介 前文已经对虚拟机进行过了简单的介绍,并且也对class文件结构,以及字节码指令进行了详尽的说明 想要了解JVM的运行机制,以及如何优化你的代码,你还需要了解一下,java编译器到底是如何编译 ...
- ncs java 成都 面试_成都java工程师面试一般都是哪些问题,基础难不难!
了解了许多学员参加的Java面试,他们表示,面试官提出的基础性问题还是比较多.从这个突破口切入,Java新手想要成功通过面试,基础知识方面也需要准备,今天达内成都Java培训班小编就来给大家分享一些J ...
- java正则表达式 匹配()_学习Java正则表达式(匹配、替换、查找)
import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; public c ...
- java核心教程_核心Java教程
java核心教程 Welcome to Core Java Tutorial. I have written a lot on Core Java and Java EE frameworks. Th ...
- java 包命名_【Java】包的命名规则
命名规范 包:所有单词的字母小写,之后每个单词用-隔开,如 org.nemo.demo 常量:所有单词的字母大写,之后每个单词用_隔开,如 FLAG 类:所有单词的首字母大写,如 TestJava p ...
- java 图片效果_使用Java进行图片底片化效果处理
使用java代码读取图片,并进行底片化处理 util importjava.awt.image.BufferedImage;importjava.io.File;importjava.util.Arr ...
- java array缓存_有java数组
[JAVA零基础入门系列]Day10 Java中的数组 [JAVA零基础入门系列](已完结)导航目录 Day1 开发环境搭建 Day2 Java集成开发环境IDEA Day3 Java基本数据类型 D ...
最新文章
- “李子柒”们的商业模式,到底需不需要微念
- Python常用技巧了解一下?
- python分块处理功能_python 对列表分块
- 奋战杭电ACM(DAY9)1013
- jQuery 源码解析笔记(一)
- 苹果X可以升级5G吗_郭明錤:苹果5G手机明年推出 屏幕最大升级至6.7寸
- 《编译原理》一道关于逆波兰式的作业题(学委推导出了逆波兰式的数学公式表示)
- 5个例子说明jQuery.extend(...)对象扩展工具方法
- Windows网络编程(基础篇1)
- 【perl脚本】单行循环正则匹配
- RYYB图像格式学习
- Windows Server 2012 R2 更改系统语言
- 千亿雅虎确认48亿贱卖,其实这些巨头比它更惨
- Java基础:方法和类详解
- VBoxGuestAdditions下载地址
- python发微信提醒天气冷了注意保暖_2019天气转冷注意保暖微信 2019天气冷注意保暖的句子(图文)...
- MySQL创建表视图及多表视图
- 二维标准Kalman滤波
- 为了营造一个看代码舒服的氛围,写了获取王者荣耀所有高清皮肤的代码,现在写代码舒心了(中途发现了一个秘密)
- SqlYog创建数据库