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

use*_*mda 6 java equals hashcode hashset

我正在尝试为我的 覆盖提到的方法HashSet

Set<MyObject> myObjectSet = new HashSet<MyObject>();
Run Code Online (Sandbox Code Playgroud)

我的对象:

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;
  }

  [...]
}
Run Code Online (Sandbox Code Playgroud)

如何覆盖 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);
}    
Run Code Online (Sandbox Code Playgroud)

我读到通过 id 进行比较是不够的,这是对象是数据库的持久实体(请参阅此处)。

名称和编号在此类型的所有对象中并不是唯一的。

那么我应该如何覆盖它?
我还需要比较里面的 hashMap 吗?

我很迷惑。该对象唯一的独特之处是在生命周期后期填充的地图 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()));
    }
Run Code Online (Sandbox Code Playgroud)

这在 compareTo 方法中失败,“对于类型 Map,此方法未定义

Jon*_*ndt 1

这里的基本问题是“如何确定两个对象是否彼此相等?”

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

正如原始问题中所述:

该对象的唯一独特之处是地图 myMap,它会在生命周期的后期填充。

给定类型的两个实例MyObject,成员变量myMap必须相互比较。这张地图属于Map<String, String>. 我立刻想到几个问题:

  • 键和值如何定义相等性?
    • (key=value对需要作为一个单位进行比较吗?)
    • (或者应该只将值相互比较?)
  • 映射中键的顺序如何影响相等性?
    • (是否应该对列表中的键进行排序,以便 ABC 等于 BCA?)
    • (或者 1-2-3 的含义与 3-2-1 不同吗?)
  • 大写/小写对值的相等性有什么不同吗?
  • 这些对象是否会存储在某种Java HashSetJava TreeSet中?
    • (是否需要在同一个集合中多次存储同一个对象?)
    • (或者具有相同哈希码的对象应该只存储一次吗?)
  • 这些对象是否需要作为列表或Java 集合的一部分进行排序?
  • 比较函数应该如何在列表中排列不相等的对象?
    • (按键顺序如何确定对象在列表中是先出现还是后出现?)
    • (值应该如何确定顺序,特别是当几个值不同时?)

每个问题的答案都会因应用程序而异。为了使其适用于一般受众,做出以下假设:

  • 为了保持确定性比较,将对键进行排序
  • 值将被视为区分大小写
  • 键和值是不可分割的,并且会作为一个单元进行比较
  • 地图将被展平为单个字符串,因此可以轻松比较结果

equals()使用、hashCode()、的美妙之compareTo()处在于,一旦hashCode()正确实现,其他功能就可以基于 来定义hashCode()

考虑到所有这些,我们有以下实现:

@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());
}
Run Code Online (Sandbox Code Playgroud)

所有关键工作都在hashCode(). 对于排序,该compareTo()函数只需要返回一个负数/零/正数——一个简单的hashCode()差异。该equals()函数只需要返回真/假——一个compareTo()等于零的简单检查。


为了进一步阅读,刘易斯·卡罗尔(Lewis Carroll)有一段关于逻辑基础的著名对话,其中涉及平等的基本问题:

https://en.wikipedia.org/wiki/What_the_Tortoise_Said_to_Achilles

而且,即使是简单的语法结构,在《爱丽丝梦游仙境》第 6 章“Pig and Pepper”的开头也有两个“相等”句子的很好的例子:

鱼男仆从腋下掏出一封大信,递给对方,语气庄严地说:“致公爵夫人。女王邀请她玩槌球。” 青蛙男仆用同样严肃的语气重复道:“来自女王。邀请公爵夫人玩槌球。” 然后他们都低下了头,卷发纠缠在一起。