Kotlin:处理集合时的异常行为

rvpgvaaj  于 2022-12-13  发布在  Kotlin
关注(0)|答案(2)|浏览(194)

我有一个简单的数据类,它存储一个位置的x和y坐标。我的用例是创建和更新这个类的单个对象,我需要维护一组唯一的坐标。
我已经在下面的代码中简化了我的用例,其中直接将pos对象添加到集合与传递对象的副本会导致不同的行为(请参见代码中的注解)。
我最初的直觉是,这可能是因为Java/Kotlin通过引用传递对象,而Set.add在引用上进行比较。然而,这似乎不是真的,如果我将pos.xpos.y设置为任何其他值,那么set.contains方法返回false。
问:如果比较是通过引用,那么为什么在设置为下面代码中给出的值以外的值时会失败?如果比较是通过哈希代码,那么为什么setByCopy在原始情况下不返回true?

data class Pos(var x: Int = 0, var y: Int = 0)

fun main() {
    val pos = Pos(0, 0)
    val set = mutableSetOf<Pos>()
    val setByCopy = mutableSetOf<Pos>()

    pos.x = -9
    pos.y = -6
    set.add(pos)
    setByCopy.add(pos.copy())

    println(pos.hashCode())

    pos.x = -8
    pos.y = -37
    // setting pos.y to any other value (e.g -35) will cause set.contains(pos) to return false.

    println(set.contains(pos))       // true, but expected false.
    println(setByCopy.contains(pos)) // false
}
djp7away

djp7away1#

通常,修改集合中的元素会产生未定义的行为。Kotlin中没有明确说明这一点,但它是从Java继承而来的,在Java中有说明:
如果使用可变对象作为集合元素,则必须非常小心。如果对象的值以影响等于比较的方式更改,而该对象是集合中的元素,则不指定集合的行为。
这意味着任何事情都可能发生:它可以随机工作或不工作。

uqzxnwby

uqzxnwby2#

你创建了两个对象,pos和一个单独的pos2,当你在一个数据类的示例上调用copy()时,你会得到一个完全独立的示例,它的属性被初始化为相同的数据。
然后将每个示例添加到一个单独的Set中。即使set包含possetByCopy包含pos2,如果调用setByCopy.contains(pos),那么它将返回 true,因为等式是如何作用于集合和数据类的:
boolean contains(Object o)
如果此集合包含指定的元素,则返回true。更正式地说,当且仅当此集合包含满足**(o==null ? e==null : o.equals(e))**的元素e时,才返回true。
o.equals(e)位是很重要的--数据类会根据它的数据自动生成equals()实现,即构造函数中的属性。所以Pos(0, 0) == Pos(0, 0)true,即使它们是不同的示例,因为它们包含相同的 data
这就是为什么setByCopy.contains(pos)为真--不是因为它包含 * 那个对象 *,而是因为它包含一个等于它的对象 *。
当您使用不同的数字更新pos时,pospos2的data属性值不同-它们不再 * 相等 *,因此setByCopy.contains(pos)返回 false
set.contains(pos)仍然评估为 true,因为 * 该集合包含pos对象 *。当您更新该对象时,集合中的引用指向同一个对象,因此它当然等于它自己!如果您希望创建一个独特的、独立的示例,并且在您更新pos时 * 不 * 更改,那么copy()就是这样做的

相关问题