ある成果物の開発を進めるに時に、「先に問題へ対応する」と「後で問題へ対応する」では、後者の方が難易度は高いです。
開発は進むにつれて周辺との因果関係が複雑化ます。全体に影響を与えない形で問題へ対応することが、難しくなるからです。
不要な再Composeを回避する対応も同じだと思います。
ですので、発生源が明確で不要な再Composeは、コーディングの段階で潰しておくべきです。
ただし、不要な再Composeの発生源を知らなければ出来ません。
ここで紹介するList型入力は、不要な再Composeの発生源の一つです。
※環境:Android Studio Koala | 2024.1.1
    Kotlin 1.9.0
    Compose Compiler 1.5.1
    androidx.compose.foundation:foundation 1.6.8
    kotlinx.collections.immutable 0.3.7
List型入力による再Composeの発生
LazyColumnで一覧(リスト)表示するデータをList型で入力している場合、再Composeがスケジュールされます。
@Stable
data class NumData(val id: Int, var num: Int)
val datas = List(12){ NumData(id = it, num = (Math.random() * 100).toInt()) }
@Composable
fun SampleListPanel() {
    Box(modifier = Modifier.fillMaxSize()) {
        val _count = remember { mutableStateOf(0) }
        SampleList(datas = datas)	  // List型で入力⇒再Composeをスケジュール
        Button(...) { Text(text = "Renew") }
        Button(						  // この階層で再Composeを実施
            modifier = Modifier.padding(5.dp).align(Alignment.TopStart),
            onClick = { _count.value ++ }
        ) { Text(text = "Compose ${_count.value}") }
    }
}
@Composable
fun SampleList(datas: List<NumData>) {	// List型で入力⇒不安定な型
    LazyColumn {
        items(items = datas) {
            ItemBox { Text(text = "Item %02d".format(it.num)) }
        }
    }
}
これは、ComposeコンパイラーがList型を不安定な型と暗黙的に認識するためです。
不安定な型を入力とするComposable関数は必ず再Composeの対象になります。

「Listの要素が変わらない」、つまり「アイテムの表示が変わらない」のであれば、この再Composeは不要です。
不要な再Composeを回避するには、List型を不変コレクションへ変換します。これには2つの方法があります。
- (1)@Immutableの指定
- (2)ImmutableListの作成
(1)再Composeを回避(@Immutableの指定)
ラッパークラスで囲み、アノテーション@Immutableを付与すると、そのクラスは不変クラスになります。
@Stable
data class NumData(val id: Int, var num: Int)
@Immutable							// ラッパーで囲み、不変クラスへ変換
data class ImmDatas(
    val values: List<NumData> = List(12){
        NumData(id = it, num = (Math.random() * 100).toInt())
    }
)
val datas = ImmDatas()
@Preview_mdpi320x480
@Composable
fun SampleListPanel() {
    Box(modifier = Modifier.fillMaxSize()) {
        val _count = remember { mutableStateOf(0) }
        SampleList(datas = datas)		// 不変⇒再Composeをスケジュールしない
        Button(...) { Text(text = "Renew") }
        Button(
            modifier = Modifier.padding(5.dp).align(Alignment.TopStart),
            onClick = { _count.value ++ }
        ) { Text(text = "Compose ${_count.value}") }
    }
}
@Composable
private fun SampleList(datas: ImmDatas) {  	// 不変⇒安定した型
    LazyColumn {
        items(items = datas.values) {
            ItemBox { Text(text = "Item %02d".format(it.num)) }
        }
    }
}

(2)再Composeを回避(ImmutableListの作成)
Listの代わりにImmutableListを使います。ImmutableListはkotlinx APIが提供する不変コレクションです。
ComposeコンパイラはImmutableListをサポートしていて、暗黙的に安定した型と認識します。
@Stable
data class NumData(val id: Int, var num: Int)
fun createDatas() = List(12){
    NumData(id = it, num = (Math.random() * 100).toInt())
}.toImmutableList()					// 不変コレクション(ImmutableList)へ変換
val datas = createDatas()
@Preview_mdpi320x480
@Composable
fun SampleListPanel() {
    Box(modifier = Modifier.fillMaxSize()) {
        val _count = remember { mutableStateOf(0) }
        SampleList(datas = datas)		// 不変⇒再Composeをスケジュールしない
        Button(...) { Text(text = "Renew") }
        Button(
            modifier = Modifier.padding(5.dp).align(Alignment.TopStart),
            onClick = { _count.value ++ }
        ) { Text(text = "Compose ${_count.value}") }
    }
}
@Composable
private fun SampleList(datas: ImmutableList<NumData>) {  // 不変⇒安定した型
    LazyColumn {
        items(items = datas) {
            ItemBox { Text(text = "Item %02d".format(it.num)) }
        }
    }
}

kotlinxのImmutableListを使う場合は、ライブラリ「kotlinx.collections.*」の追加が必要です。「Maven Central」から提供されています。
詳細は「Immutable Collections Library for Kotlin」を参照してださい。
関連記事:
