KotlinにおけるListは、コレクションの中でも最も頻繁に使われる基本要素です。
しかし一方で、「Listは不変なのか?」「MutableListとの違いは?」といった点で誤解されやすいテーマでもあります。
この記事では、
- KotlinのListの正確な位置づけ
- 読み取り専用と変更可能の違い
- Kotlinらしい安全な使い方
- 実務での注意点
を順序立てて、仕様に忠実かつ実践的に解説します。
KotlinのListとは
KotlinのListは、順序を持つ要素の集合を表すコレクションです。
主な特徴は次の通りです。
- 要素には インデックス(0始まり) が割り当てられる
- 同じ値を複数回保持できる
- Kotlinでは 「読み取り専用」と「変更可能」 が型として明確に分かれている
この「読み取り専用」という概念が、Java経験者にとって最も重要な違いになります。
ListとMutableListの決定的な違い
Kotlinには、用途の異なる2種類のListがあります。
読み取り専用List(List)
val fruits = listOf("Apple", "Banana", "Orange")
特徴
- この参照からは 追加・削除・更新ができない
addやremoveといった操作は コンパイル時に禁止される- 安全な参照用インターフェース
重要なのは、List は 「不変(immutable)」ではなく「読み取り専用(read-only)」 である点です。
つまり、
- この変数を通じては変更できない
- しかし、元のデータが別の参照から変更される可能性はある
という位置づけになります。
読み取り専用 = 絶対に変わらない、ではない
val mutable = mutableListOf(1, 2, 3)
val readOnly: List<Int> = mutable
mutable.add(4)
println(readOnly) // [1, 2, 3, 4]
readOnly は List 型ですが、中身は変化しています。
これはKotlinのListが 「操作制限されたビュー」 であるためです。
変更可能なList(MutableList)
val mutableFruits = mutableListOf("Apple", "Banana")
特徴
- 要素の追加・削除・更新が可能
- 状態が変化する処理向き
- UI状態管理や編集処理で使用される
使い分けの指針(実務的)
| 利用シーン | 推奨型 |
|---|---|
| 参照専用 | List |
| 内部で要素を変更 | MutableList |
| 関数の引数 | 原則 List |
| 外部APIとして公開 | List |
| 状態を持つ処理 | MutableList |
「変更が必要になるまでMutableListを使わない」これがKotlinらしい設計です。
Listの基本操作
要素の取得
val fruits = listOf("Apple", "Banana", "Orange")
fruits[0] // Apple
fruits.get(1) // Banana
インデックスが範囲外の場合は IndexOutOfBoundsException が発生します。
サイズ取得
fruits.size
要素の存在確認
"Apple" in fruits
fruits.contains("Pear")
MutableListの操作
要素の追加
val list = mutableListOf("A", "B")
list.add("C")
list.add(1, "X")
要素の削除
list.remove("B")
list.removeAt(0)
要素の更新
list[0] = "Z"
KotlinらしいList操作(高階関数)
KotlinのList操作は、高階関数を使うことで非常に簡潔かつ安全になります。
map(変換)
val numbers = listOf(1, 2, 3)
val doubled = numbers.map { it * 2 }
// [2, 4, 6]
- 元のListは変更されない
- 新しいListが生成される
filter / filterNot(抽出)
val even = numbers.filter { it % 2 == 0 }
// [2]
val withoutTwo = numbers.filterNot { it == 2 }
// [1, 3]
find / firstOrNull
numbers.find { it > 1 } // 2
numbers.firstOrNull() // 1
※ find は firstOrNull { 条件 } とほぼ同義です。
any / all
numbers.any { it > 2 } // true
numbers.all { it > 0 } // true
Listの生成方法いろいろ
空のList
val empty = emptyList<String>()
サイズ指定生成
val list = List(5) { it * 2 }
// [0, 2, 4, 6, 8]
生成されるのは 読み取り専用のList です。
配列からListへ変換
val array = arrayOf(1, 2, 3)
val list = array.toList()
Listとnullの扱い(Kotlin特有)
要素がnullを許容するList
val list: List<String?> = listOf("A", null, "B")
List自体がnull
val list: List<String>? = null
この2つは意味がまったく異なります。
「Listがnullなのか」「要素がnullなのか」 を常に意識することが重要です。
ListとSequenceの違い
numbers
.asSequence()
.filter { it > 1 }
.map { it * 2 }
.toList()
| 種類 | 特徴 |
|---|---|
| List | 即時評価・中間Listが生成される |
| Sequence | 遅延評価・必要な分だけ処理 |
※ 小規模データではSequenceが遅くなる場合もあるため、「大量データや複雑なパイプライン処理向け」 と理解するのが適切です。
実務でよくある注意点
MutableListを外部に公開しない
非推奨
fun getItems(): MutableList<String>
推奨
fun getItems(): List<String>
内部ではMutableListでも、外部にはListとして公開するのが安全です。
ループ中の削除に注意
for (item in list) {
list.remove(item) // 例外の原因
}
安全な代替案
再生成する方法(推奨)
val newList = list.filterNot { it == "A" }
MutableListとして必要な場合
val newList = list.filterNot { it == "A" }.toMutableList()
まとめ
- Kotlinの
Listは 読み取り専用インターフェース - 「不変」と「読み取り専用」は別概念
- 変更が必要な場合のみ
MutableListを使う - 高階関数を使うことで安全かつ簡潔に書ける
- 外部公開は常に
Listを基本とする
以上、Kotlinのリストについてでした。
最後までお読みいただき、ありがとうございました。










