当前位置: 首页 > 知识库问答 >
问题:

如何覆盖HashSet的equals()、hashcode()和compareTo()

公羊奇
2023-03-14

我试图覆盖提到的方法为我的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方法中失败,“这个方法对于类型映射是未定义的

共有3个答案

令狐凌
2023-03-14

这就是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?

萧和平
2023-03-14

compareTo()与排序相关。它与HashSetHashMap无关。

正常工作的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的方法。

楚和悌
2023-03-14

这里的基本问题是“如何确定两个物体是否相等?”

这是一个简单对象的简单问题。然而,即使是稍微复杂一点的对象也变得越来越困难。

如原问题所述:

该对象唯一独特的地方是在生命周期后期填充的地图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键的对象可能会导致存储的值“丢失”。