KotlinとJavaのリスト操作でハマらないために：toListとsortedByの挙動と注意点

### 対象者
* KotlinとJavaを併用する方
* Kotlin初心者

## 1.はじめに👀
Kotlinでリスト操作をする際、toListやsortedByの挙動について意識したことはありますか？
これらの関数は、**要素数によって返す具体的な型が異なる**ため、Javaと連携するプロジェクトでは注意が必要です。
この記事では、KotlinとJavaの視点を交えながら以下の内容を解説します。

* KotlinのtoListとsortedByの挙動
* Java連携時の注意点
* テストケースの重要性
 
## 2.KotlinのtoListとsortedByの挙動☝️

### 2.1 toList()の挙動
Kotlinの toList は、コレクションから 不変リスト (List<T>) を作成します。しかし、**要素数によって実際に返される型は異なります。**

実例

```kotlin
fun main() {
   
    val single = sequenceOf(1).toList()
    val multiple = sequenceOf(1,2,3).toList()
    
    println(single::class) // class java.util.Collections$SingletonList
    println(multiple::class) // class java.util.ArrayList
}
```

* 要素が1つのときはSingletonList
* 要素が複数のときはArrayList

ただし、KotlinのtoListが返す型はあくまで **List< T >** として扱われるため、変更操作(例：addやremove)は許可されていません。

```Kotlin
public fun <T> Iterable<T>.toList(): List<T> {
    if (this is Collection) {
        return when (size) {
            0 -> emptyList()
            1 -> listOf(if (this is List) get(0) else iterator().next())
            else -> this.toMutableList()
        }
    }
    return this.toMutableList().optimizeReadOnlyList()
}
```
- - -

### 2.2 sortedBy()の挙動

sortedByもtoListと同様に、**要素数によって返される型が異なります**。

実例
```Kotlin
fun main() {
   
    val single = listOf(1).sortedBy{ it }
    val multiple = listOf(1,2,3).sortedBy{ it }
    
    println(single::class) // class java.util.Collections$SingletonList
    println(multiple::class) // class java.util.Arrays$ArrayList
}
```

* 要素数が1つの場合はSingletoList
* 要素数が複数の場合はArrays$ArrayList


```Kotlin
public inline fun <T, R : Comparable<R>> Iterable<T>.sortedBy(crossinline selector: (T) -> R?): List<T> {
    return sortedWith(compareBy(selector))
}

public fun <T> Iterable<T>.sortedWith(comparator: Comparator<in T>): List<T> {
    if (this is Collection) {
       if (size <= 1) return this.toList()
       @Suppress("UNCHECKED_CAST")
       return (toTypedArray<Any?>() as Array<T>).apply { sortWith(comparator) }.asList()
    }
    return toMutableList().apply { sortWith(comparator) }
}
```

これもList<T>として返されるため、不変リストとしての振る舞いをします。変更操作を行おうとするとエラーになります。

### 2.3 Kotlinリスト設計の背景

Kotlinの公式ドキュメントでは不変コレクションがデフォルトとして提供されています。
https://kotlinlang.org/docs/collections-overview.html



## 3.Java連携時の注意点⚠️

Kotlinが返すリスト(toListやsortedByの結果)は不変リストとして設計されています。
しかし、Javaではその内部実装に依存して動作が異なります。

### 3.1Kotlinの不変リストに対する変更操作

**Kotlinコード**
```Kotlin
fun createSortedList(): List<Int> {
    return listOf(3,1,2).sortedBy(){ it }
}
``` 

**Javaコード**
```Java
List<Integer> list = KotlinClassKt.createSortedList();
list.add(4); // UnsupportedOperationExceptionが発生
```

複数要素のsortedByの内部実装は`Arrays.ArrayList`のため、addやremoveを実行するとUnsupportedOperationExceptionが発生します。

Arrays.ArrayListはjava.util.ArrayListと似ていますが、java.util.Arrays内部で作られる一時的なリストで、固定サイズのリストとして動作します。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Arrays.html

Kotlinからの操作ではList<T>の不変性を守りますが、Javaからアクセスするときの挙動に注意が必要です。

### 3.2 Javaからアクセスする場合の矛盾点

**toList**の要素数が複数件の場合、内部実装がArrayListを返すため、**Java側では変更可能なリストのように扱えます。**

**Kotlinコード**
```Kotlin
fun createList(): List<Int> {
    return sequenceOf(1, 2, 3).toList()
}
``` 

**Javaコード**
```Java
List<Integer> list = KotlinClassKt.createList();
list.add(4); // 実行可能(エラーなし)
``` 

KotlinとJavaでリストの扱いが矛盾する可能性があります。

Kotlinでは不変リストとして設計されていますが、Javaからアクセスすると、リストの変更が試みられることがあります。これはKotlinとJavaのAPI設計の違いに起因しています。

:::note warn
Kotlin側でtoListを使った場合、返されるリストが実際に変更可能であるとは限らない場合もあります。
:::

**KotlinとJavaで挙動に矛盾が生じないよう、不変リストをJava側でも守るべきです。**


## 4.テストケースの重要性🧪

**要素数やシナリオごとに挙動を確認するテスト**を用意することで意図しない動作を防ぐことが大切です。

例
```Kotlin
fun main(){
 val single = listOf(1).sortedBy { it }
 val multiple = listOf(3, 1, 2).sortedBy { it }

 // 要素数1の挙動を確認
 assert(single == listOf(1))

 // 要素数複数の挙動を確認
 assert(multiple == listOf(1, 2, 3))

```


## 5.まとめ
* KotlinのtoListやsortedByは不変リストを返しますが、要素数によって内部実装が異なる

* JavaからKotlinのリストを扱う際、不変リストと変更可能リストを区別する必要がある

* テストケースを使って挙動を確認することで、予期せぬ動作を防ぐ

KotlinのListは不変がデフォルトで、JavaのListとは挙動が異なります。この違いを意識することが大切です。
何か間違ったことを言っている場合はご指摘よろしくお願いします。