【问题标题】:How to write the hashCode() function for a cyclic graph node?如何为循环图节点编写 hashCode() 函数?
【发布时间】:2013-12-19 15:54:29
【问题描述】:

我有以下类用作图表的一部分:

public class MyNode {

    private String name;

    private Set<MyNode> parents;

    private Set<MyNode> children;

    // getters and setters
}

当我使用 Eclipse 的Source / Generate hashCode() and equals() 时,它会生成这个方法:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((children == null) ? 0 : children.hashCode());
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    result = prime * result + ((parents == null) ? 0 : parents.hashCode());
    return result;
}

问题是这个方法从当前对象到它的孩子,然后在计算第一个孩子的hashCode()时,它通过parents.hashCode()回到原来的节点但是不知道 hashCode() 已经在那里计算过了。然后它重新进入原始节点的children,它给出了一个漂亮的无限循环。

问题:如何检查MyNode 的两个实例是否是同一个对象,同时避免无限循环?为了停止探索,在 MyNode 类中添加 visited 布尔值是否可以接受?还是有更好的解决方案?

谢谢!

【问题讨论】:

  • visited 必须改回来。

标签: java eclipse hashcode


【解决方案1】:

在实现hashCodeequals 时,不应使用节点的子节点或父节点。它们是可变属性。 hashCode 的计算在更改边缘后会有所不同,并会导致 Maps 和 Set 集合损坏。

仅使用不可变字段来实现hashCodeequals,这些字段为对象创建不同的自然键。一个名字可能是一个很好的候选人,除非它可以改变。

如果不存在不可变字段,则:

  1. 不要覆盖hashCodeequals 方法,默认就可以了。它将使每个类实例都是唯一的。
  2. 创建一些唯一的 id 字段(按顺序)并将其用作 hashCode 中的种子并在 equals 中进行比较。

【讨论】:

  • 但是如果两个节点具有相同的不可变字段但不在图中的同一位置怎么办?
  • 嗯...在大多数应用程序中,默认值 not 很好,因为默认实现基本上为每个实例提供了不同的数字。 IE。没有两个实例是相同的,即使它们具有完全相同的数据。
  • +1 到 Stropboskop。 equals() 和 hashcode() 相互遵守合同非常重要。在这种情况下,我认为您只想对节点名称进行哈希处理。
  • @geceo 如果两个节点具有相同的关键字段,这意味着它应该是同一个节点。这是决定对象的关键字段是什么的问题,而不是equalshashCode 的实现。
  • @Stroboskop 如果您没有适合计算equalshashCode 的字段,则默认是好的。如果您没有关键字段,则每个实例都是不同的。
【解决方案2】:

您可以使用== 运算符检查两个对象是否是同一个实例:

if(obj0 == obj1)

如果您想使用 visited 标志,您应该使用查找来实现它(例如 Map),因为在您的代码运行后,您必须重置这些标志。

我建议忽略parents/children,而是为每个节点添加一个唯一的id

【讨论】:

  • 如果在 try 中清除了 "visited" 标志,他可能会使用访问和缓存值侥幸{ /*recurse*/ } finally {visited=false;}
【解决方案3】:

hashCode() 的唯一硬性要求是两个相等的对象必须返回相同的哈希值。

此实现将遵守:

@Override
public int hashCode() {
    return 42;
}

...因为没有什么说不相等的对象必须有不同的哈希码。

当然,为了正确地完成它的工作,hashCode() 会更好地工作,如果值可以分散。只要你不违反关于平等对象的规则,你可以做任何你喜欢的事情。在我不希望哈希对性能至关重要的普通通用代码中,我通常只返回一个或两个重要字段的哈希码(例如本例中的名称),只要这些字段参与计算相等,使得相等的对象具有相同的哈希值。

关键是,您确实需要将equalshashCode 视为一对,最好从您对等于的定义开始。

【讨论】:

  • @geceo 请注意危险,您的节点集将变得非常慢,因为所有节点都将保存在 Set 实现中哈希表的一个桶中。
  • 当然——我不是建议他实际使用“42”实现,我只是用它作为例子来证明hashCode不必复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-07
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 2015-12-17
  • 1970-01-01
相关资源
最近更新 更多