我认为如果你有一个包含在Java Set中的对象<> (或作为Map<>中的关键字),用于确定身份或关系的任何字段(通过hashCode()
,equals()
,compareTo()
等)都不能更改时没有对集合上的操作造成未指定的行为? (编辑:this other question中提到的)
(换句话说,这些字段应该是不可变的,或者您应该要求从集合中删除对象,然后更改,然后重新插入。)
我问的原因是我正在阅读Hibernate Annotations reference guide,并且有一个例子,其中有HashSet<Toy>
但Toy
类有字段name
和{{ 1}}这是可变的,也用于serial
计算......一个红旗在我脑海中消失了,我只是想确定我理解它的含义。
答案 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测试中会怎样?