Java集中对象的可变字段

时间:2009-07-02 20:58:19

标签: java collections identity mutable

我认为如果你有一个包含在Java Set中的对象<> (或作为Map<>中的关键字),用于确定身份或关系的任何字段(通过hashCode()equals()compareTo()等)都不能更改时没有对集合上的操作造成未指定的行为? (编辑:this other question中提到的)

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

我问的原因是我正在阅读Hibernate Annotations reference guide,并且有一个例子,其中有HashSet<Toy>Toy类有字段name和{{ 1}}这是可变的,也用于serial计算......一个红旗在我脑海中消失了,我只是想确定我理解它的含义。

4 个答案:

答案 0 :(得分:8)

Set的javadoc说

  

注意:如果必须非常小心   可变对象用作集合   元素。集合的行为不是   指定对象的值是否为   改变了影响的方式   等于对象的比较   集合中的元素。一个特例   这个禁令是不是   允许集合包含   本身就是一个元素。

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

答案 1 :(得分:3)

这是正确的,它可能会导致一些定位地图条目的问题。官方的行为是未定义的,因此如果将其添加到哈希集或作为哈希映射中的键,则不应更改它。

答案 2 :(得分: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

答案 3 :(得分:0)

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

您还可以改变IdentityHashMap内部的对象 - 除了对象标识之外的任何内容都用于定位内容。

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