【问题标题】:mutable fields for objects in a Java SetJava Set 中对象的可变字段
【发布时间】:2009-07-02 20:58:19
【问题描述】:

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

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

我问的原因是我正在阅读Hibernate Annotations reference guide,它有一个示例,其中有一个HashSet<Toy>,但Toy 类具有字段nameserial,它们是可变的并且也被使用在hashCode() 的计算中......我的脑海中出现了一个危险信号,我只是想确保我理解它的含义。

【问题讨论】:

    标签: java collections identity mutable


    【解决方案1】:

    Set 的 javadoc 说

    注意:如果出现以下情况,必须非常小心 可变对象作为集合使用 元素。集合的行为不是 指定对象的值是否为 以影响的方式改变 等于对象时的比较 集合中的一个元素。一个特殊情况 这项禁令的原因是它不是 允许集合包含 本身作为一个元素。

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        是的,那会导致坏事发生。

        // 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
        

        【讨论】:

        • 等等,我才意识到这是一个非常糟糕的例子。因为我仍然持有引用,所以最后一个 contains() 实际上会返回 true。 HashMaps 就另当别论了,不过三天的周末就快要下班了,我不想再挖个例子了。
        • 呵呵,等你回来再发点东西——我很期待。
        • 再等等,我忘记了HashSet使用HashMap作为backing。这确实可能发生:后备的 HashMap 在对桶中的元素检查 equals() 之前使用 hashCode 跳转到桶,更改元素会导致它跳转到错误的桶,并且找不到任何元素。跨度>
        【解决方案4】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多