Why are my sets of data objects not equal?

I was just surprised by a failing test assert (using kotlin.test here):

data class MyType {
    var values : MutableSet<OtherType> = mutableSetOf()
}
data class OtherType(val value: String) {}

assertEquals(setOf(OtherType(myValue)), myObject.values)

The error looks like this:

expected: java.util.Collections$SingletonSet<[OtherType(value=myvalue)]> but was: java.util.LinkedHashSet<[OtherType(value=myvalue)]>

Clearly, the test should have been successful: I only care about value equality and not at all about which set implementation is used.

How do I express this best in Kotlin?

Ideas

  • Use Guava: assertTrue(Sets.symmetricDifference(setOf(OtherType(myValue)), myObject.values).isEmpty())
    rather lengthy, though, and un-mathy colleagues might not understand it
  • Create a new assertion function like assertEqualContent for comparing sets by content.
  • Re-define == on sets (or all collections?): it should ignore the concrete type of the set itself.

For now, I went with 2 implemented using 1. A solution without Guava would be nice, though.

You code is incomplete and even doesn’t compile. Cannot help you until more info.

assertEquals compares these sets as equal in the code like yours, same as the equality operator.

import kotlin.test.assertEquals

fun main(args: Array<String>) {
//sampleStart
    data class OtherType(val value: String) {}
    class MyType {
        var values : MutableSet<OtherType> = mutableSetOf()
    }
    val myValue = "test"
    val myObject = MyType().apply { values.add(OtherType(myValue)) }
    
    println(setOf(OtherType(myValue))::class)
    println(myObject.values::class)
    println(setOf(OtherType(myValue)) == myObject.values)
    assertEquals(setOf(OtherType(myValue)), myObject.values)
//sampleEnd
}

So the reason why you see the other result must be something else.

1 Like

Ahh… that is what I get for not actually running the MWE. My bad; the setup looked too obvious. Should’ve learned that lesson a while ago. :'D

Here’s an actual MWE; it’s not about the sets at all:

data class OtherType(val value: String) {
    // Set by GORM
    var id: Long? = null
    var version: Int? = null

    override fun equals(other: Any?): Boolean {
        return when (other) {
            !is OtherType -> false
            id == other.id && value == other.value -> true
            else -> false
        }
    }
}

fun main(args: Array<String>) {
    val myValue = "test"
    println(OtherType(myValue) == OtherType(myValue))
}

Now that I actually looked at OtherType.equals, the problem is apparent – if you’ve seen Kotlin Puzzlers 2018 (code). It only looks correct (might be a Swift-ism). With

override fun equals(other: Any?): Boolean {
    return when (other) {
        is OtherType -> id == other.id && value == other.value
        else -> false
    }
}

everything works as expected, including set comparison.

Thanks for your help! (And for submitting that puzzler to Anton!)

Oh, yeah. That one looks familiar :joy:

1 Like