我试图覆盖提到的方法为我的HashSet
:
Set<MyObject> myObjectSet = new HashSet<MyObject>();
MyObject:
public class MyObject implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
int number;
Map<String,String> myMap;
public MyObject(String name, int number, Map<String,String> myMap) {
this.name = name;
this.number = number;
this.myMap = myMap;
}
[...]
}
如何重写hashcode(),equals()和compareTo()方法?
目前我有以下几点:
public int hashCode () {
return id.hashCode();
}
// override the equals method.
public boolean equals(MyObject s) {
return id.equals(s.id);
}
// override compareTo
public int compareTo(MyObject s) {
return id.compareTo(s.id);
}
我读到通过id比较是不够的,这是对象是数据库的持久实体(见这里)。
此类型的所有对象的名称和编号不是唯一的。
那么我应该如何覆盖它呢?
我还需要比较它里面的hashMap吗?
我很困惑。该对象唯一独特的地方是map myMap,它将在生命周期的后期填充。
我如何检查它是否相等?
根据所有的回答,我将方法改为
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyComplexObj myComplexObj = (MyComplexObj) o;
return myMap != null ? myMap.equals(myComplexObj.myMap) : myComplexObj.myMap == null;
}
@Override
public int hashCode() {
return myMap != null ? myMap.hashCode() : 0;
}
public int compareTo(MyComplexObj o) {
return myMap.compareTo(o.getMyMap()));
}
这在compareTo方法中失败,“这个方法对于类型映射是未定义的
这就是intellij默认选项提供的功能
import java.util.Map;
public class MyObject {
String name;
int number;
Map<String,String> myMap;
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyObject myObject = (MyObject) o;
if (number != myObject.number) return false;
if (name != null ? !name.equals(myObject.name) : myObject.name != null) return false;
return myMap != null ? myMap.equals(myObject.myMap) : myObject.myMap == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + number;
result = 31 * result + (myMap != null ? myMap.hashCode() : 0);
return result;
}
}
但是自从你说
该对象唯一独特的地方是在生命周期后期填充的地图myMap。
我只想保留myMap,跳过名称和数字(但这就引出了一个问题,为什么要在收藏的所有元素中包含一个冗余的数据——名称和数字?)
然后就变成了
import java.util.Map;
public class MyObject {
String name;
int number;
Map<String,String> myMap;
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyObject myObject = (MyObject) o;
return myMap != null ? myMap.equals(myObject.myMap) : myObject.myMap == null;
}
@Override
public int hashCode() {
return myMap != null ? myMap.hashCode() : 0;
}
}
请记住,equals和hashcode方法还有其他方法。例如,以下是intelliJ为代码生成提供的选项
与Equals和Hashcode不同,compareTo和任何其他行为之间不存在契约。你其实不需要用compareTo做任何事情,直到你想利用它进行排序。阅读更多关于CompareTo的信息为什么Java类应该实现comparable?
compareTo()
与排序相关。它与HashSet
或HashMap
无关。
正常工作的equals()
和hashCode()
对于基于哈希的集合的成员至关重要。在Javadoc中阅读它们的Object
规范。
约书亚·布洛赫(Joshua Bloch)的《高效Java》中可能给出了实现这些功能的明确建议。我建议你阅读相关章节——很容易用谷歌搜索。在这里试图解释这一切是没有意义的。
您可能没有注意到的一件事是,您的字段myMap
有一个自己的工作equals()
和hashCode()
,因此您不必对其进行任何特殊操作。如果你能保证没有一个字段是null,一个合理的hashCode()
将是(遵循Bloch的系统):
public int hashCode() {
int result = 44; // arbitrarily chosen
result = 31 * result + (int) (id ^ (id >>> 32));
result = 31 * result + name.hashCode();
result = 31 * result + number;
result = 31 * result + myMap.hashCode();
return result;
}
(如果这些代码中的任何一个可能为空,您将需要更多代码)
几乎所有的IDE都会使用类中的所有字段自动生成equals()
和hashcode()
。他们将使用与布洛赫的建议非常相似的东西。搜索用户界面。你会找到的。
另一种选择是使用Apache ReflectionUtils,它允许您简单地使用:
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(final Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
这计算出在运行时使用哪些字段,并应用Bloch的方法。
这里的基本问题是“如何确定两个物体是否相等?”
这是一个简单对象的简单问题。然而,即使是稍微复杂一点的对象也变得越来越困难。
如原问题所述:
该对象唯一独特的地方是在生命周期后期填充的地图myMap。
给定类型MyObject
的两个实例,成员变量myMap
必须相互比较。此地图属于map类型
钥匙怎么
(列表中的键是否应该排序,以便A-B-C等同于B-C-A?)
- (或者1-2-3的意思与3-2-1不同?)
(是否需要在同一个集合中多次存储同一对象?)
(或者具有相同哈希代码的对象应该只存储一次?)
(键顺序应该如何确定对象在列表中的出现时间是早还是晚?)
这些问题的答案会因应用程序而异。为了使其适用于一般受众,做出了以下假设:
为了保持确定性比较,将对键进行排序
- 值将被视为区分大小写
- 键和值是不可分割的,将作为一个单元进行比较
- 地图将被展平成一个字符串,因此结果可以很容易地进行比较
使用
equals()。
考虑到所有这些,我们有以下实施:
@Override
public boolean equals(final Object o)
{
if (o instanceof MyObject)
{
return (0 == this.compareTo(((MyObject) o)));
}
return false;
}
@Override
public int hashCode()
{
return getKeyValuePairs(this.myMap).hashCode();
}
// Return a negative integer, zero, or a positive integer
// if this object is less than, equal to, or greater than the other object
public int compareTo(final MyObject o)
{
return this.hashCode() - o.hashCode();
}
// The Map is flattened into a single String for comparison
private static String getKeyValuePairs(final Map<String, String> m)
{
final StringBuilder kvPairs = new StringBuilder();
final String kvSeparator = "=";
final String liSeparator = "^";
if (null != m)
{
final List<String> keys = new ArrayList<>(m.keySet());
Collections.sort(keys);
for (final String key : keys)
{
final String value = m.get(key);
kvPairs.append(liSeparator);
kvPairs.append(key);
kvPairs.append(kvSeparator);
kvPairs.append(null == value ? "" : value);
}
}
return 0 == kvPairs.length() ? "" : kvPairs.substring(liSeparator.length());
}
所有的关键工作都在
hashCode()
内部完成。对于排序,compareTo()
函数只需要返回一个负数/零/正数——一个简单的hashCode()
diff.,equals()
函数只需要返回真/假——compareTo()
等于零的简单检查。
为了进一步阅读,刘易斯·卡罗尔有一段关于逻辑基础的著名对话,涉及平等的基本问题:
https://en.wikipedia.org/wiki/What_the_Tortoise_Said_to_Achilles
而且,就连简单的语法结构而言,在第6章“猪和胡椒”的开头有一个很好的例子,来自《爱丽丝梦游仙境》:
鱼仆人从腋下拿出一封伟大的信开始,他把这封信交给了另一个人,用庄严的语气说:“给公爵夫人的。女王邀请我玩槌球。"青蛙仆人用同样严肃的语气重复道:“女王送的。邀请公爵夫人玩槌球。"然后他们俩都低头鞠躬,卷发缠绕在一起。
我像这样初始化哈希集: 我的对象(抽象类的子类)的和方法如下所示: 我甚至编写了一个简单的单元测试,它不会给出任何错误: 在我的应用程序中,我有一个方法,我只需将对象添加到: 在另一种方法中,我检查
问题内容: 好的,我从很多地方和来源都听说过,每当我覆盖equals()方法时,我也需要覆盖hashCode()方法。但是请考虑以下代码 这里的输出为true,完全按照我想要的方式为false,我根本不关心重写hashCode()方法。这意味着hashCode()覆盖是一种选择,而不是每个人都说的强制性选择。 我想要第二次确认。 问题答案: 它对您有用,因为您的代码未使用任何需要API的功能(Ha
我正在覆盖两个int的简单容器对象的equals和hashcode方法。每个int反映另一个对象的索引(该对象是什么并不重要)。该类的目的是表示两个对象之间的连接。 连接的方向无关紧要,因此equals方法应该返回true,无论两个整数在对象中以何种方式循环。 这是我所拥有的(从整数的源代码修改): 但我的问题是:有没有更好的方法来实现这一点? 我主要担心的是hashcode()方法会为任何两个相
问题内容: 如果我有 如果我比较A的2个实例但没有覆盖equals方法,是否可以获得预期的结果? 问题答案: 如果我比较A的2个实例但没有覆盖equals方法,是否可以获得预期的结果? 这取决于您的期望:) 默认实现将为您提供 引用相等性-换句话说,当您比较两个引用时,仅当它们是对同一对象的引用时才返回true。 通常,您将重写以实现“值相等”,在这种情况下,两个不同的对象通常被认为具有相等的字段
我在为自定义员工类实现hashmap时有问题。 > 我只重写了employee类的。所以,对于相同的对象,我得到相同的hashcode值。如果我不重写equals(),那就没问题了,对吧?因为object类的)比较了引用(在这里,我得到了相同的引用来表示相等的对象)。无论如何,根据我的逻辑,我永远不会为不同的emp对象得到相同的哈希值。所以,让
问题内容: 为什么StringBuffer的/ StringBuilder的没有覆盖,从对象的方法? 请给我建议清晰的图片,以帮助理解问题… 问题答案: 因为是可变的,所以它的主要用途是 构造 字符串。如果要比较内容,请调用并比较返回的值。 覆盖可变对象通常没有用,因为修改用作a键的对象可能会导致存储的值“丢失”。