我需要实现hashCode()和equals()方法吗?

3
如果我有一个地图和一个对象作为地图键,那么默认的哈希和相等方法就足够了吗?
class EventInfo{

    private String name;
    private Map<String, Integer> info
 }

然后我想创建一个地图:
Map<EventInfo, String> map = new HashMap<EventInfo, String>();

我需要明确地实现hashCode()和equals()吗?谢谢。


2
默认的equals和hashcode方法只使用对象的地址。如果您想让两个等效的对象比较相等(这样您就可以在地图中找到插入的对象),则需要显式方法。 - Hot Licks
4个回答

6

是的,你需要。 HashMap 的工作原理是通过计算键的哈希码并将其用作基准点来实现的。如果 hashCode 函数没有被覆盖(您自己),则它将使用内存地址,而 equals 将与 == 相同。

如果您使用 Eclipse,则可以让它为您生成。单击 菜单 → 生成 hashCode() 和 equals()

如果您没有 Eclipse,下面是一些应该有效的代码。(我在 Eclipse 中生成了这些代码,如上所述。)

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

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (!(obj instanceof EventInfo)) {
        return false;
    }
    EventInfo other = (EventInfo) obj;
    if (info == null) {
        if (other.info != null) {
            return false;
        }
    } else if (!info.equals(other.info)) {
        return false;
    }
    if (name == null) {
        if (other.name != null) {
            return false;
        }
    } else if (!name.equals(other.name)) {
        return false;
    }
    return true;
}

我在Eclipse中自动生成了代码。太酷了!不需要任何修改! - user697911
@user697911:我很高兴能提供帮助。我注意到你已经问了20个问题,但是还没有接受任何答案。如果你找到了一个适合你的答案,请考虑点击答案旁边的绿色勾号(轮廓)或至少给它一个好评。接受答案还可以获得声望值! - wchargin
@user697911 你可能需要创建一个账户才能接受答案,我不确定。无论如何,我建议你这样做,因为它将提升你在 SO 社区的地位,表明你是“来留下”的,而不仅仅是问一个问题就离开。此外,几乎任何用户名都比 user697911 好 :) - wchargin
1
你知道的,看着这个,我不确定自动生成的equals()方法是否正确。如果“other”是“EventInfo”的子类,那怎么办? - Edward Falk

3

是的,你需要它们,否则你将无法比较两个EventInfo(并且你的映射将无法工作)。


2
严格来说,不是的。hashCode()和equals()的默认实现会产生应该有效的结果。请参见http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode() 我理解的是hashCode()的默认实现通过获取对象在内存中的地址并转换为整数来工作,equals()的默认实现仅在两个对象实际上是同一个对象时返回true。
在实践中,您可能可以(并且应该)改进这两种实现。例如,两种方法都应忽略不重要的对象成员。此外,equals()可能希望递归比较对象中的引用。
在您的特定情况下,如果两个对象引用相同的字符串或两个字符串相等且两个映射相同或相等,则可以将equals()定义为true。我认为WChargin给出了非常好的实现。

2

这取决于您想要发生什么。如果两个不同的EventInfo实例具有相同的nameinfo,应该产生两个不同的键,则无需实现equalshashCode

所以

EventInfo info1 = new EventInfo();
info1.setName("myname");
info1.setInfo(null);
EventInfo info2 = new EventInfo();
info2.setName("myname");
info2.setInfo(null);

info1.equals(info2)会返回false,info1.hashCode()的值也与info2.hashCode()不同。

因此,在将它们添加到地图中时:

map.put(info1, "test1");
map.put(info2, "test2");

您会有两个不同的条目。

现在,这可能是期望的行为。例如,如果您的EventInfo正在收集不同的事件,则具有相同数据的两个不同事件可能希望成为两个不同的条目。

equalshashCode协定也适用于Set

例如,如果您的事件信息包含鼠标点击,则很可能希望最终得到:

Set<EventInfo> collectedEvents = new HashSet<EventInfo>();
collectedEvents.add(info1);
collectedEvents.add(info2);

不只有一个收集事件,而是有两个...

希望我表达清楚了...

编辑:

如果上述的set和map只应该包含单个条目,则可以使用Apache Commons的EqualsBuilderHashCodeBuilder来简化equalshashCode的实现:

@Override
public boolean equals(Object obj) {
    if (obj instanceof EventInfo) {
        EventInfo other = (EventInfo) obj;
        EqualsBuilder builder = new EqualsBuilder();
        builder.append(name, other.name);
        builder.append(info, other.info);
        return builder.isEquals();
    }
    return false;
}

@Override
public int hashCode() {
    HashCodeBuilder builder = new HashCodeBuilder();
    builder.append(name);
    builder.append(info);
    return builder.toHashCode();
}

编辑2:

如果两个EventInfo实例具有相同的名称,则将它们视为相同可能是合适的,例如,如果name是某个唯一标识符(我知道对于您特定的对象来说有点牵强,但我在这里进行概括...)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接