2009-07-02 38 views
10

我正确的假设,如果你有一个对象包含在一个Java集<>(或作为一个关键在地图<>这个问题),任何领域是用于确定身份或关系(通过hashCode()equals()compareTo()等)不能在没有对采集造成不确定的行为进行操作改变了吗? (编辑:作为暗示在this other question可变字段的对象在Java集

(换句话说,这些领域要么是不可变的,或者如果您需要将对象从集合中删除,然后改,然后重新插入。)

的我想问的原因是我正在阅读Hibernate Annotations reference guide,它有一个例子,其中HashSet<Toy>Toy类的字段nameserial是可变的,也用于计算hashCode() ......红旗在我的头和我只是想确保我理解它的含义。

回答

7

Set的Javadoc说

注:如果 可变对象用作设置 元素大,一定要小心。一组的行为不是 指定某个对象的值是 在影响 equals比较而对象是 集合中的元素的方式改变。这种禁止的特殊情况是 ,它不是 允许对一组含有 本身作为一个元素。

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

3

这是正确的,它可能会导致一些问题,定位映射条目。正式的行为是未定义的,所以如果你将它添加到哈希集或哈希表中的键,你不应该改变它。

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 
+0

等等,我只是意识到这是一个非常糟糕的例子。由于我仍然持有该引用,所以最后的contains()实际上将返回true。 HashMaps是另一回事,但它几乎是在三天的周末上放弃时间,我不想挖掘一个例子。 – 2009-07-02 21:33:30

0

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

也可以发生变异是一个IdentityHashMap中的对象 - 无非对象标识等用于定位的内容。

即使你可以做这些事情与这些资格,他们使你的代码更加脆弱。如果有人希望稍后更改为TreeSet,或将该可变字段添加到hashCode /等于测试中,该怎么办?