3

如果我有一个映射和一个对象作为映射键,那么默认的 hash 和 equals 方法是否足够?

class EventInfo{

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

然后我想创建一个地图:

Map<EventInfo, String> map = new HashMap<EventInfo, String>();

我必须明确实现 hashCode() 和 equals() 吗?谢谢。

4

4 回答 4

6

是的你是。HashMaps 通过计算密钥的哈希码并将其用作基点来工作。如果该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;
}
于 2013-03-12T00:44:10.467 回答
3

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

于 2013-03-12T00:39:45.140 回答
2

严格来说,没有。hashCode() 和 equals() 的默认实现将产生应该工作的结果。请参阅http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode()

我的理解是 hashCode() 的默认实现通过获取对象在内存中的地址并转换为整数来工作,而 equals() 的默认实现仅在两个对象实际上是同一个对象时才返回 true。

在实践中,您可以(并且应该)改进这两种实现方式。例如,这两种方法都应该忽略不重要的对象成员。此外,equals() 可能希望递归地比较对象中的引用。

在您的特定情况下,如果两个对象引用相同的字符串或两个字符串相等并且两个映射相同或它们相等,您可以将 equals() 定义为 true。我认为 WChargin 给了你很好的实现。

于 2013-03-12T00:45:59.160 回答
2

取决于你想要发生什么。如果两个不同EventInfo的实例具有相同的nameandinfo应该导致两个不同的键,那么你不需要实现equalsand hashCode

所以

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

info1.equals(info2)将返回 falseinfo1.hashCode()并将返回不同的值info2.hashCode()

因此,当您将它们添加到地图时:

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

你会有两个不同的条目。

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

equalsandhashCode合同也适用于Seta 。

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

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

2 个收集的事件,而不仅仅是 1 个......

希望我在这里有意义...

编辑:

但是,如果上面的 set 和 map 应该只包含一个条目,那么您可以使用 apache commons EqualsBuilderHashCodeBuilder来简化equalsand的实现hashCode

@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是一些唯一标识符(我知道它与您的特定对象有点牵强,但我在这里概括... )

于 2013-03-12T00:47:56.487 回答