12

我是否正确假设如果您有一个包含在 Java Set<> 中的对象(或作为 Map<> 中的键),任何用于确定身份或关系的字段(通过hashCode(),equals()compareTo().) 不能在不导致对集合的操作出现未指定行为的情况下进行更改?(编辑:正如在另一个问题中提到的)

(换句话说,这些字段应该是不可变的,或者您应该要求将对象从集合中删除,然后更改,然后重新插入。)

我问的原因是我正在阅读Hibernate Annotations 参考指南,它有一个示例,其中有一个HashSet<Toy>Toy该类具有字段name并且serial是可变的并且也用于hashCode()计算......我脑海中出现了一个危险信号我只是想确保我理解它的含义。

4

4 回答 4

8

javadocSet

注意:如果将可变对象用作集合元素,则必须非常小心。如果对象的值以影响等于比较的方式更改,而对象是集合中的一个元素,则不指定集合的​​行为。此禁令的一个特殊情况是不允许集合包含自身作为元素。

这仅仅意味着您可以在集合中使用可变对象,甚至可以更改它们。您只需确保更改不会影响Set查找项目的方式。因为HashSet,这将不需要更改用于计算的字段hashCode()

于 2009-07-02T22:08:28.210 回答
3

这是正确的,它可能会导致定位地图条目的一些问题。官方的行为是未定义的,所以如果你将它添加到一个 hashset 或作为 hashmap 中的一个键,你不应该改变它。

于 2009-07-02T21:04:53.643 回答
1

是的,这会导致不好的事情发生。

// Given that the Toy class has a mutable field called 'name' which is used
// in equals() and hashCode():
Set<Toy> toys = new HashSet<Toy>();
Toy toy = new Toy("Fire engine", ToyType.WHEELED_VEHICLE, Color.RED);
toys.add(toy);
System.out.println(toys.contains(toy)); // true
toy.setName("Fast truck");
System.out.println(toys.contains(toy)); // false
于 2009-07-02T21:07:35.057 回答
0

在 HashSet/HashMap 中,您可以改变包含的对象以更改compareTo()操作结果——相对比较不用于定位对象。但这在 TreeSet/TreeMap 中是致命的。

您还可以改变 IdentityHashMap 中的对象——除了对象身份之外,没有任何东西可以用来定位内容。

即使您可以使用这些资格来做这些事情,它们也会使您的代码更加脆弱。如果有人想稍后更改为 TreeSet,或者将该可变字段添加到 hashCode/equality 测试中怎么办?

于 2009-07-03T02:45:18.187 回答