Collectionをプロパティに持つdataクラスの等価判定(Kotlin)

タイトル通り、Collectionをプロパティに持つdataクラスで、 `==` による等価判定の挙動が気になったため、試してみました。

dataクラスは、デフォルトだとプロパティ同士の比較がされ、一致する場合はtrueになるはず。

# List

`User` というdataクラスを用意して、`ListGroup` というdataクラスのプロパティとして`List<User>` を持たせました。

```kotlin
data class User(
    val id: Int,
    val name: String
)
```

```kotlin
data class ListGroup(
    val id: Int,
    val users: List<User>
)
```

以下のテストケースを書いてみました。

```kotlin

import org.junit.Test

import org.junit.Assert.*

class ListEqualityTest {
    @Test
    fun `リストの要素が同じ場合`() {
        val groupA = ListGroup(1, listOf(User(1, "Ken"), User(2, "Mike")))
        val groupB = ListGroup(1, listOf(User(1, "Ken"), User(2, "Mike")))
        assertEquals(groupA, groupB)
    }
    @Test
    fun `リストの要素が違う場合`() {
        val groupA = ListGroup(1, listOf(User(1, "Ken"), User(2, "Mike")))
        val groupB = ListGroup(1, listOf(User(1, "Ken"), User(3, "Jane")))
        assertNotEquals(groupA, groupB)
    }
    @Test
    fun `リストの要素の順番が違う場合`() {
        val groupA = ListGroup(1, listOf(User(1, "Ken"), User(2, "Mike")))
        val groupB = ListGroup(1, listOf(User(2, "Mike"), User(1, "Ken")))
        assertNotEquals(groupA, groupB)
    }
}
```

いずれもテストは通り、リストの比較は、**要素同士の比較になる** ようで、自分としては期待通りの動作でした。リスト同士の比較をすることになるので、当然と言えば当然か。。

# Map

続いてMapですが、これも期待通りで、テストケースは全て通りました。

```kotlin
data class MapGroup(
    val id: Int,
    val users: Map<Int, User>
)
```

```kotlin
import org.junit.Assert.*
import org.junit.Test

class MapEqualityTest {
    @Test
    fun `Mapの要素が同じ場合`() {
        val groupA = MapGroup(1, mapOf(
            1 to User(1, "Ken"),
            2 to User(2, "Mike")))
        val groupB = MapGroup(1, mapOf(
            1 to User(1, "Ken"),
            2 to User(2, "Mike")))
        assertEquals(groupA, groupB)
    }
    @Test
    fun `valueが異なる場合`() {
        val groupA = MapGroup(1, mapOf(
            1 to User(1, "Ken"),
            2 to User(2, "Mike")))
        val groupB = MapGroup(1, mapOf(
            1 to User(1, "Ken"),
            2 to User(2, "Jane")))
        assertNotEquals(groupA, groupB)
    }
    @Test
    fun `key valueの要素が1つ多い場合`() {
        val groupA = MapGroup(1, mapOf(
            1 to User(1, "Ken"),
            2 to User(2, "Mike")))
        val groupB = MapGroup(1, mapOf(
            1 to User(1, "Ken"),
            2 to User(2, "Mike"),
            3 to User(3, "Jane")))
        assertNotEquals(groupA, groupB)
    }
}
```

# Set

Setも同様。

```kotlin
data class SetGroup(
    val id: Int,
    val users: Set<User>
)
```

```kotlin
import org.junit.Test

import org.junit.Assert.*

class SetEqualityTest {
    @Test
    fun `要素が同じ場合`() {
        val groupA = SetGroup(1, setOf(User(1, "Ken"), User(2, "Mike")))
        val groupB = SetGroup(1, setOf(User(1, "Ken"), User(2, "Mike")))
        assertEquals(groupA, groupB)
    }
    @Test
    fun `要素が異なる場合`() {
        val groupA = SetGroup(1, setOf(User(1, "Ken"), User(2, "Mike")))
        val groupB = SetGroup(1, setOf(User(1, "Ken"), User(3, "Jane")))
        assertNotEquals(groupA, groupB)
    }
}
```

# おまけ(Array)

配列に関しては正しく（というか自分が期待する通りに）判定してくれません。
IDE上でも、注意されます。
`Array property in data class: it's recommended to override equals() / hashCode() `

```kotlin
data class ArrayGroup(
    val id: Int,
    val users: Array<User>
)
```

```kotlin
import org.junit.Test

import org.junit.Assert.*

class ArrayEqualityTest {
    @Test
    fun `配列の要素が同じ場合`() {
        // これは失敗する
        val groupA = ArrayGroup(1, arrayOf(User(1, "Ken"), User(2, "Mike")))
        val groupB = ArrayGroup(1, arrayOf(User(1, "Ken"), User(2, "Mike")))
        assertEquals(groupA, groupB)
    }
}
```

このテストは失敗します。比較を定義したい場合はequals()をオーバーライドして、自分で定義しましょう。

# 参考

[Kotlin メモ : data class で List はいい感じに処理してくれるけど Array おまえはダメだ](http://y-anz-m.blogspot.com/2017/07/kotlin-data-class-list-array.html)