Kotlin の for 文は、for (変数 in 範囲やコレクション) { 処理 } の形で書きます。 Java の for (int i = 0; i < n; i++) にあたる書き方はなく、0..<n や 0 until n のように「範囲」を作って、その中を順に取り出します。

この記事では、範囲の作り方10種類を「書き方・含む範囲・実行出力」の早見表にまとめ、Java の for 文との対応も並べました。10..1 と書くと1回も回らない、step に 0 を渡すと例外になる、forEach の中の return は関数ごと終わる、といった引っかかりやすい点は、実行結果とエラー文で確かめています。while・do-while・repeat、break / continue のラベルまで、このページで引けるようにしました。

確認環境:Ubuntu 24.04 / Kotlin 2.4.20(kotlinc)/ OpenJDK 21.0.10。掲載した出力とエラー文は、すべてこの環境でコンパイル・実行して得たものです。

for 文の書き方 早見表

items は listOf("A", "B", "C") です。

書き方 含む範囲 実行出力 Java での書き方
for (i in 1..5) 1 以上 5 以下(5 を含む) 1 2 3 4 5 for (int i = 1; i <= 5; i++)
for (i in 1..<5) 1 以上 5 未満(5 を含まない) 1 2 3 4 for (int i = 1; i < 5; i++)
for (i in 1 until 5) 1 以上 5 未満(..< と同じ) 1 2 3 4 for (int i = 1; i < 5; i++)
for (i in 5 downTo 1) 5 から 1 まで下がる(1 を含む) 5 4 3 2 1 for (int i = 5; i >= 1; i--)
for (i in 1..10 step 3) 1 から 3 ずつ増える 1 4 7 10 for (int i = 1; i <= 10; i += 3)
for (i in 10 downTo 1 step 4) 10 から 4 ずつ下がる 10 6 2 for (int i = 10; i >= 1; i -= 4)
for (i in (1..5).reversed()) 1..5 を逆順に 5 4 3 2 1 for (int i = 5; i >= 1; i--)
for (s in items) 要素を先頭から A B C for (String s : items)
for (i in items.indices) 添字 0 から size - 1 まで 0 1 2 for (int i = 0; i < items.size(); i++)
for (i in 0..items.lastIndex) 同上(lastIndex は size - 1) 0 1 2 同上
for ((i, s) in items.withIndex()) 添字と要素の組 0=A 1=B 2=C 添字の for と items.get(i)
for (s in items.reversed()) 要素を末尾から C B A 添字を size - 1 から下げる for
for (i in items.lastIndex downTo 0) 添字を末尾から 2 1 0 for (int i = items.size() - 1; i >= 0; i--)
repeat(3) { ... } 0 から 2 までの3回(it に回数が入る) 0 1 2 for (int i = 0; i < 3; i++)

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Kotlin for文の書き方 until・downTo・step早見表」

まず動かす:for 文の基本形

次のコードを First.kt という名前で保存します。

fun main() {
    // 1 から 5 まで(5 を含む)
    for (i in 1..5) {
        println("i = $i")
    }

    // リストの要素を順に取り出す
    val fruits = listOf("りんご", "みかん", "ぶどう")
    for (fruit in fruits) {
        println(fruit)
    }

    // 本体が1文なら波かっこを省ける
    for (i in 1..3) print(i * i)
    println()
}

コンパイルして実行します。-include-runtime は Kotlin の標準ライブラリを jar に含める指定、-Dstdout.encoding=UTF-8 は日本語の出力を UTF-8 にそろえる指定です。

kotlinc First.kt -include-runtime -d first.jar
java -Dstdout.encoding=UTF-8 -jar first.jar
i = 1
i = 2
i = 3
i = 4
i = 5
りんご
みかん
ぶどう
149

ポイントは3つです。

  • ループ変数(i や fruit)に val や var は書きません。 型も範囲や要素から決まります。
  • ループ変数は読み取り専用です。ループの中で i = i + 1 のように代入するとコンパイルエラーになります(後述)。
  • for は「in の右側にあるものから、1つずつ取り出す」構文です。数を数えるときも、1..5 という範囲から取り出しています。

早見表の書き方をすべて実行する

早見表の出力は、次のコードで得たものです。文字(Char)や Long の範囲も加えています。

fun main() {
    val items = listOf("A", "B", "C")

    print("1..5            : "); for (i in 1..5) print("$i "); println()
    print("1..<5           : "); for (i in 1..<5) print("$i "); println()
    print("1 until 5       : "); for (i in 1 until 5) print("$i "); println()
    print("5 downTo 1      : "); for (i in 5 downTo 1) print("$i "); println()
    print("1..10 step 3    : "); for (i in 1..10 step 3) print("$i "); println()
    print("10 downTo 1 step 4: "); for (i in 10 downTo 1 step 4) print("$i "); println()
    print("(1..5).reversed(): "); for (i in (1..5).reversed()) print("$i "); println()
    print("items           : "); for (s in items) print("$s "); println()
    print("items.indices   : "); for (i in items.indices) print("$i "); println()
    print("0..items.lastIndex: "); for (i in 0..items.lastIndex) print("$i "); println()
    print("items.withIndex(): "); for ((i, s) in items.withIndex()) print("$i=$s "); println()
    print("items.reversed(): "); for (s in items.reversed()) print("$s "); println()
    print("lastIndex downTo 0: "); for (i in items.lastIndex downTo 0) print("$i "); println()
    print("repeat(3)       : "); repeat(3) { print("$it ") }; println()
    print("'a'..'e'        : "); for (c in 'a'..'e') print("$c "); println()
    print("'a'..'i' step 2 : "); for (c in 'a'..'i' step 2) print("$c "); println()
    print("1L..3L          : "); for (n in 1L..3L) print("$n "); println()
}
1..5            : 1 2 3 4 5 
1..<5           : 1 2 3 4 
1 until 5       : 1 2 3 4 
5 downTo 1      : 5 4 3 2 1 
1..10 step 3    : 1 4 7 10 
10 downTo 1 step 4: 10 6 2 
(1..5).reversed(): 5 4 3 2 1 
items           : A B C 
items.indices   : 0 1 2 
0..items.lastIndex: 0 1 2 
items.withIndex(): 0=A 1=B 2=C 
items.reversed(): C B A 
lastIndex downTo 0: 2 1 0 
repeat(3)       : 0 1 2 
'a'..'e'        : a b c d e 
'a'..'i' step 2 : a c e g i 
1L..3L          : 1 2 3 

「..」と「..<」と until の違い

書き方 終わりの値 向いている場面
a..b 含む 「1 から 10 まで」のように両端を数える
a..<b 含まない 「0 から size 未満」のように個数で回す
a until b 含まない ..< と同じ結果

..< は終わりを含まない範囲を作る演算子です。Kotlin 公式の「What's new in Kotlin 1.9.0」によると、1.7.20 で導入され 1.8.0 で安定版になりました。until も同じ範囲を作るので、どちらで書いても出力は変わりません。記号のほうが「未満」であることが見た目で伝わるので、新しく書くコードでは ..< が読みやすいでしょう。

リストを添字で回すときは、0..<list.size と書くよりも list.indices が短く、範囲の書き間違いも起きません。

Java の for 文との対応

Java の for (初期化; 条件; 更新) の形は Kotlin にはありません。早見表の「Java での書き方」の列を実際に Java 21 で動かし、Kotlin と同じ出力になることを確かめました。

import java.util.List;

public class Loops {
    public static void main(String[] args) {
        List<String> items = List.of("A", "B", "C");
        for (int i = 1; i <= 5; i++) System.out.print(i + " ");
        System.out.println();
        for (int i = 1; i < 5; i++) System.out.print(i + " ");
        System.out.println();
        for (int i = 5; i >= 1; i--) System.out.print(i + " ");
        System.out.println();
        for (int i = 1; i <= 10; i += 3) System.out.print(i + " ");
        System.out.println();
        for (int i = 10; i >= 1; i -= 4) System.out.print(i + " ");
        System.out.println();
        for (String s : items) System.out.print(s + " ");
        System.out.println();
        for (int i = 0; i < items.size(); i++) System.out.print(i + "=" + items.get(i) + " ");
        System.out.println();
        for (int i = items.size() - 1; i >= 0; i--) System.out.print(i + " ");
        System.out.println();
        for (int i = 0; i < 3; i++) System.out.print(i + " ");
        System.out.println();
    }
}
1 2 3 4 5 
1 2 3 4 
5 4 3 2 1 
1 4 7 10 
10 6 2 
A B C 
0=A 1=B 2=C 
2 1 0 
0 1 2 

Java の書き方をそのまま Kotlin に書くと、syntax error: Expecting 'in'. をはじめとする構文エラーが並びます。Java から移すときは、次の順で考えると置き換えやすくなります。

Java の条件・更新 Kotlin の範囲
i <= b; i++ a..b
i < b; i++ a..<b
i >= b; i-- a downTo b
i += n 範囲の後ろに step n
i -= n a downTo b step n
条件や更新が上のどれにも当てはまらない while 文で書く

引っかかりやすい点を実行結果で確かめる

fun main() {
    // 1. 10..1 は空の範囲。1回も回らない
    var count = 0
    for (i in 10..1) count++
    println("10..1 の回数: $count  isEmpty=${(10..1).isEmpty()}")
    println("10 downTo 1: ${(10 downTo 1).toList()}")

    // 2. step は正の数だけ。0 や負の数は例外
    for (s in listOf(0, -1)) {
        try {
            for (i in 1..10 step s) print(i)
        } catch (e: IllegalArgumentException) {
            println("step $s -> ${e::class.simpleName}: ${e.message}")
        }
    }

    // 3. step で割り切れないと、終わりの値は出てこない
    println("1..10 step 4 = ${(1..10 step 4).toList()}  last=${(1..10 step 4).last}")

    // 4. until / ..< で始まりと終わりが同じなら空
    println("0 until 0 = ${(0 until 0).toList()}  0..<0 = ${(0..<0).toList()}")

    // 5. 範囲は最初に1回だけ評価される
    var n = 3
    for (i in 1..n) {
        n = 10
        print("$i ")
    }
    println(" (n=$n)")

    // 6. downTo + step と、step + reversed() は同じにならないことがある
    println("10 downTo 1 step 4 = ${(10 downTo 1 step 4).toList()}")
    println("(1..10 step 4).reversed() = ${(1..10 step 4).reversed().toList()}")

    // 7. 空のリストの indices / lastIndex
    val empty = emptyList<String>()
    println("indices=${empty.indices} lastIndex=${empty.lastIndex}")
    for (i in 0..empty.lastIndex) println("来ない")
}
10..1 の回数: 0  isEmpty=true
10 downTo 1: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
step 0 -> IllegalArgumentException: Step must be positive, was: 0.
step -1 -> IllegalArgumentException: Step must be positive, was: -1.
1..10 step 4 = [1, 5, 9]  last=9
0 until 0 = []  0..<0 = []
1 2 3  (n=10)
10 downTo 1 step 4 = [10, 6, 2]
(1..10 step 4).reversed() = [9, 5, 1]
indices=0..-1 lastIndex=-1
番号 起きること こう書くとよい
1 10..1 は空の範囲で、1回も回らない(エラーにもならない) 下がるときは 10 downTo 1
2 step 0 や step -1 は IllegalArgumentException(Step must be positive) step には正の数。向きは downTo で決める
3 1..10 step 4 は 1, 5, 9。10 は出てこない 終わりの値が必要なら、範囲と刻みを見直す
4 0 until 0 と 0..<0 は空 空のリストでもそのまま回せる
5 範囲は最初に1回だけ評価される。途中で n を変えても回数は変わらない 回数が途中で変わる処理は while で書く
6 10 downTo 1 step 4 は 10, 6, 2、(1..10 step 4).reversed() は 9, 5, 1 どちらの端から数えるかで書き分ける
7 空のリストの indices は 0..-1、lastIndex は -1 0..lastIndex も空の範囲になり、安全に回せる

1番は Java の感覚で最も間違えやすい点です。.. は「小さいほうから大きいほうへ」しか進まないので、始まりが終わりより大きいと空の範囲になります。コンパイルエラーにも例外にもならないので、「ループの中が1回も動かない」ときは、まず範囲の向きを確認してください。

2番の step は、負の数で「逆向き」を表すことはできません。逆向きは downTo で表し、step には常に正の数を書きます。

List・配列・文字列・Map を for で回す

fun main() {
    // List
    val names = listOf("佐藤", "鈴木", "高橋")
    for (name in names) print("$name ")
    println()
    for (i in names.indices) print("$i:${names[i]} ")
    println()
    for ((i, name) in names.withIndex()) print("$i:$name ")
    println()

    // 配列(Array / IntArray)
    val scores = intArrayOf(70, 85, 90)
    for (s in scores) print("$s ")
    println()
    for (i in scores.indices) scores[i] += 5      // 添字で書き換える
    println(scores.joinToString())

    // 文字列は1文字ずつ(Char)
    for (ch in "Kotlin") print("[$ch]")
    println()
    for ((i, ch) in "abc".withIndex()) print("$i=$ch ")
    println()

    // Map はキーと値に分解して受け取る
    val stock = mapOf("ペン" to 12, "ノート" to 0, "付箋" to 30)
    for ((item, qty) in stock) println("$item: $qty")
    for (key in stock.keys) print("$key ")
    println()
    for (value in stock.values) print("$value ")
    println()
    for (entry in stock) print("${entry.key}=${entry.value} ")
    println()

    // Set
    for (x in setOf(3, 1, 3, 2)) print("$x ")
    println()

    // 2つのリストを同時に回す
    val prices = listOf(120, 300, 80)
    for ((name, price) in names.zip(prices)) print("$name=$price ")
    println()

    // 2次元
    val grid = listOf(listOf(1, 2, 3), listOf(4, 5, 6))
    for (row in grid) {
        for (cell in row) print("$cell ")
        println()
    }
}
佐藤 鈴木 高橋 
0:佐藤 1:鈴木 2:高橋 
0:佐藤 1:鈴木 2:高橋 
70 85 90 
75, 90, 95
[K][o][t][l][i][n]
0=a 1=b 2=c 
ペン: 12
ノート: 0
付箋: 30
ペン ノート 付箋 
12 0 30 
ペン=12 ノート=0 付箋=30 
3 1 2 
佐藤=120 鈴木=300 高橋=80 
1 2 3 
4 5 6 
対象 要素だけ 添字つき
List / Set for (x in list) for ((i, x) in list.withIndex())
配列(Array・IntArray など) for (x in array) for (i in array.indices)
文字列 for (ch in text)(1文字ずつ Char) for ((i, ch) in text.withIndex())
Map for ((key, value) in map) for (entry in map) で entry.key / entry.value
2つのリストを同時に for ((a, b) in listA.zip(listB)) ―
  • 要素を書き換えたいときは添字で回します。 for (s in scores) の s は読み取り専用なので、配列の中身を変えるには scores[i] += 5 のように添字を使います。
  • Map の for ((item, qty) in stock) は、キーと値の組を2つの変数に分けて受け取る書き方(分解宣言)です。mapOf で作った Map は、追加した順に取り出されました。
  • Set は重複を除いた要素を回します。上の例では setOf(3, 1, 3, 2) が 3 1 2 になっています。

Kotlin の公式ドキュメントでは、範囲と配列を対象にした for 文は添字ベースのループにコンパイルされ、イテレータのオブジェクトは作られないと説明されています。for (i in 0..<n) と書いても、範囲のオブジェクトを毎回作る心配はいりません。

break と continue、ラベルで外側のループを指定する

fun main() {
    // break:ループを抜ける
    for (i in 1..10) {
        if (i == 4) break
        print("$i ")
    }
    println("<- break")

    // continue:今の回だけ飛ばす
    for (i in 1..6) {
        if (i % 2 == 0) continue
        print("$i ")
    }
    println("<- continue")

    // ラベルなし:内側のループだけ抜ける
    for (i in 1..3) {
        for (j in 1..3) {
            if (j == 2) break
            print("($i,$j) ")
        }
    }
    println("<- ラベルなし break")

    // break@ラベル:外側のループごと抜ける
    outer@ for (i in 1..3) {
        for (j in 1..3) {
            if (i == 2 && j == 2) break@outer
            print("($i,$j) ")
        }
    }
    println("<- break@outer")

    // continue@ラベル:外側のループの次の回へ進む
    outer@ for (i in 1..3) {
        for (j in 1..3) {
            if (j == 2) continue@outer
            print("($i,$j) ")
        }
    }
    println("<- continue@outer")
}
1 2 3 <- break
1 3 5 <- continue
(1,1) (2,1) (3,1) <- ラベルなし break
(1,1) (1,2) (1,3) (2,1) <- break@outer
(1,1) (2,1) (3,1) <- continue@outer
書き方 動き
break いちばん内側のループを抜ける
continue いちばん内側のループの次の回へ進む
break@outer outer@ を付けたループを抜ける
continue@outer outer@ を付けたループの次の回へ進む

ラベルは 名前@ の形でループの前に書き、使う側は break@名前 と書きます。名前は自由に決められます。二重ループで「見つかったら全体を終える」処理は、フラグ用の変数を用意しなくても break@outer の1行で書けます。

forEach の中の return と return@forEach の違い

forEach は for 文ではなく、ラムダを受け取る関数です。そのため、中で return を書いたときの動きが for 文と違います。

val nums = listOf(1, 2, 3, 4, 5)

// return:外側の関数 a() ごと終わる(「done」は出ない)
fun a() {
    nums.forEach {
        if (it == 3) return
        print("$it ")
    }
    println("done")
}

// return@forEach:その回のラムダだけ終わる(continue に相当)
fun b() {
    nums.forEach {
        if (it == 3) return@forEach
        print("$it ")
    }
    println("done")
}

// 無名関数:return は無名関数だけを終える(continue に相当)
fun c() {
    nums.forEach(fun(value: Int) {
        if (value == 3) return
        print("$value ")
    })
    println("done")
}

// 途中で打ち切りたい(break に相当):run で囲んでラベルへ戻る
fun d() {
    run loop@{
        nums.forEach {
            if (it == 3) return@loop
            print("$it ")
        }
    }
    println("done")
}

// 途中で打ち切るなら for と break が素直
fun e() {
    for (n in nums) {
        if (n == 3) break
        print("$n ")
    }
    println("done")
}

// for の中のラムダ(インライン関数)からは break / continue が使える
fun f() {
    val items = listOf("10", "x", "30", "end", "50")
    for (s in items) {
        val n = s.toIntOrNull() ?: run {
            if (s == "end") break
            continue
        }
        print("$n ")
    }
    println("done")
}

fun main() {
    print("a: "); a(); println()
    print("b: "); b()
    print("c: "); c()
    print("d: "); d()
    print("e: "); e()
    print("f: "); f()
    print("forEachIndexed: ")
    nums.forEachIndexed { i, n -> print("$i=$n ") }
    println()
}
a: 1 2 
b: 1 2 4 5 done
c: 1 2 4 5 done
d: 1 2 done
e: 1 2 done
f: 10 30 done
forEachIndexed: 0=1 1=2 2=3 3=4 4=5 
関数 書いたもの 動き for 文でいうと
a return forEach を呼んでいる関数 a() ごと終わる。done は出ない 関数の return
b return@forEach その回のラムダだけ終わり、次の要素へ進む continue
c 無名関数の中の return 無名関数だけ終わり、次の要素へ進む continue
d run loop@{ ... } で囲んで return@loop forEach 全体を抜けて、次の行へ進む break
e for 文と break ループを抜けて、次の行へ進む break

forEach の中に break や continue をそのまま書くと、コンパイルエラーになります。

// E2.kt
fun main() {
    listOf(1, 2, 3).forEach {
        if (it == 2) break
        println(it)
    }
}
E2.kt:4:22: error: 'break' and 'continue' are only allowed inside loops.
        if (it == 2) break
                     ^^^^^

途中で抜けたい・飛ばしたい処理は for 文、最後まで全要素に同じ処理をするなら forEach と使い分けると、ラベルを書かずに済みます。

一方、関数 f のように for 文の中に置いたラムダ(run などのインライン関数に渡すもの)からは、外側の for に対する break と continue が使えます。この書き方は「What's new in Kotlin 2.2.0」で安定版になったと案内されています。s.toIntOrNull() ?: run { ... continue } のように、値が取れなかったときだけ後始末をして次へ進む、という処理が書きやすくなりました。

while・do-while・repeat

回数が先に決まらない繰り返しは while、回数だけ決まっているなら repeat が向いています。

fun main() {
    // while:条件を先に調べる
    var n = 100
    var steps = 0
    while (n > 1) {
        n /= 2
        steps++
    }
    println("while: steps=$steps n=$n")

    // 条件が最初から false なら1回も実行しない
    var a = 0
    while (a > 0) { println("来ない") }

    // do-while:必ず1回は実行し、あとで条件を調べる
    var b = 0
    do {
        println("do-while: b=$b")
        b++
    } while (b < 0)

    // do の中で宣言した変数を条件で使える
    var total = 0
    do {
        val next = total + 4
        total = next
    } while (next < 10)
    println("total=$total")

    // repeat:回数だけ決まっているとき。it は 0 始まり
    repeat(3) { println("repeat: $it") }

    // イテレータを自分で進める(for の中身と同じ動き)
    val it = listOf("x", "y").iterator()
    while (it.hasNext()) print(it.next())
    println()
}
while: steps=6 n=1
do-while: b=0
total=12
repeat: 0
repeat: 1
repeat: 2
xy
構文 条件を調べるタイミング 最低の実行回数 向いている場面
for (x in ...) ― 0回 範囲やコレクションを順に処理する
while (条件) { } 本体の前 0回 条件を満たすあいだ続ける
do { } while (条件) 本体の後 1回 まず1回実行してから続けるか決める
repeat(n) { } ― n 回ちょうど 回数だけ決まっていて、範囲を書くまでもない

do-while では、do の波かっこの中で宣言した変数を、while の条件で使えます(上の例の next)。「読み取ってから、その値で続けるか決める」処理をすっきり書けます。

ループ中にリストを変更する・結果を集める

for で回している最中のリストから要素を削除すると、実行時に例外になります。

fun main() {
    val list = mutableListOf(1, 2, 3, 4, 5, 6)
    try {
        for (x in list) {
            if (x % 2 == 0) list.remove(x)
        }
    } catch (e: ConcurrentModificationException) {
        println("ループ中の削除: ${e::class.qualifiedName}")
    }

    val list2 = mutableListOf(1, 2, 3, 4, 5, 6)
    list2.removeAll { it % 2 == 0 }
    println("removeAll: $list2")

    val list3 = mutableListOf(1, 2, 3, 4, 5, 6)
    for (i in list3.lastIndex downTo 0) {
        if (list3[i] % 2 == 0) list3.removeAt(i)
    }
    println("後ろから removeAt: $list3")

    // 結果を集めるだけなら for より関数が短い
    val squares = (1..5).map { it * it }
    val evens = (1..10).filter { it % 2 == 0 }
    println("$squares $evens sum=${(1..100).sum()}")
}
ループ中の削除: java.util.ConcurrentModificationException
removeAll: [1, 3, 5]
後ろから removeAt: [1, 3, 5]
[1, 4, 9, 16, 25] [2, 4, 6, 8, 10] sum=5050

条件に合う要素を消すなら removeAll { 条件 }、添字で消すなら後ろから回すと、消したあとの添字のずれを気にせずに済みます。

また、「結果を集める」「絞り込む」「合計する」だけなら、for 文を書くより関数のほうが短くなります。

やりたいこと for 文で書くと 関数で書くと
各要素を変換した一覧を作る 空のリストを用意して add list.map { ... }
条件に合う要素だけ集める if で判定して add list.filter { ... }
合計を出す var に足していく list.sum()
途中で抜ける・飛ばす break / continue for 文のままが読みやすい
添字が必要 withIndex() / indices forEachIndexed { i, x -> ... }

小数の刻みと、自作クラスを for で回す

小数の範囲はそのままでは for で回せません。

// E1.kt
fun main() {
    for (x in 0.0..1.0) println(x)
}
E1.kt:3:15: error: for-loop range must have an 'iterator()' method.
    for (x in 0.0..1.0) println(x)
              ^^^^^^^^

for が回せるのは、iterator() を持つものです。0.0..1.0 は「含まれるかどうか」を調べる範囲としては使えますが、次の値を決める刻みがないので取り出せません。小数の刻みが必要なときは、整数で回して計算します。

// iterator() を operator で用意すれば、自作クラスも for で回せる
class Countdown(private val from: Int) {
    operator fun iterator() = object : Iterator<Int> {
        var current = from
        override fun hasNext() = current > 0
        override fun next() = current--
    }
}

fun main() {
    for (n in Countdown(3)) print("$n ")
    println()
    // 小数の刻みは整数で回して計算する
    for (i in 0..4) print("${i * 0.25} ")
    println()
    // generateSequence でも書ける
    for (x in generateSequence(1) { it * 2 }.takeWhile { it < 100 }) print("$x ")
    println()
}
3 2 1 
0.0 0.25 0.5 0.75 1.0 
1 2 4 8 16 32 64 

operator fun iterator() を用意し、その戻り値が hasNext() と next() を持っていれば、自分で作ったクラスも for 文で回せます。

エラー文から引く早見表

書いたコード エラー文・例外(Kotlin 2.4.20) 直し方
for (i in 1..3) { i = i + 1 } 'val' cannot be reassigned. ループ変数は書き換えず、別の変数を使う
for (x in 0.0..1.0) for-loop range must have an 'iterator()' method. 整数で回して計算する
for (i in 5) method 'iterator()' is ambiguous for this expression. …(省略)… 0..<5 や 1..5 のように範囲を書く
for (int i = 0; i < 5; i++) syntax error: Expecting 'in'. …(省略)… for (i in 0..<5) と書く
forEach { if (...) break } 'break' and 'continue' are only allowed inside loops. for 文にするか、return@forEach を使う
for (i in 1..10 step 0) 実行時に IllegalArgumentException: Step must be positive, was: 0. step には正の数を書く
ループ中に list.remove(x) 実行時に java.util.ConcurrentModificationException removeAll { } か、後ろから removeAt

1行目は次のコードで確認しました。

// E3.kt
fun main() {
    for (i in 1..3) {
        i = i + 1
    }
}
E3.kt:4:9: error: 'val' cannot be reassigned.
        i = i + 1
        ^

よくある質問

Q. until と「..<」はどちらを使えばよいですか? A. 結果は同じです(どちらも 1 2 3 4 でした)。..< は記号で「未満」が伝わり、.. と見た目がそろうので、これから書くコードでは ..< が読みやすいでしょう。以前から until で書かれているコードを急いで書き換える必要はありません。

Q. 逆順に回すには downTo と reversed() のどちらがよいですか? A. 数の範囲なら 5 downTo 1 が短く読めます。リストの要素を逆順に取り出すなら for (s in items.reversed())、添字が必要なら for (i in items.lastIndex downTo 0) が向いています。

Q. for 文のループ変数の型はどこで決まりますか? A. in の右側から決まります。1..5 なら Int、'a'..'e' なら Char、1L..3L なら Long、文字列のリストなら String です。

Q. forEach と for 文はどちらを使えばよいですか? A. 全要素に同じ処理をするだけなら、どちらでも同じ結果になります。途中で抜ける・飛ばす処理があるなら、break と continue がそのまま使える for 文が素直です。forEach では return が関数ごと終わる点に注意します。

まとめ

  • for 文は for (変数 in 範囲やコレクション) の形だけ。 Java の「初期化; 条件; 更新」は範囲に置き換える
  • .. は終わりを含み、..< と until は含まない。 添字で回すなら indices、添字と要素の両方なら withIndex()
  • 下がるときは downTo。 10..1 は空の範囲で1回も回らず、エラーにもならない
  • step は正の数だけ。 0 や負の数は IllegalArgumentException。刻みが合わないと終わりの値は出てこない
  • break@ラベル / continue@ラベルで外側のループを指定できる
  • forEach の中の return は関数ごと終わる。 次の要素へ進むなら return@forEach、途中で抜けるなら for 文と break
  • 回数が先に決まらないなら while、まず1回実行するなら do-while、回数だけなら repeat

参考・出典

  • Kotlin 公式ドキュメント「Conditions and loops」 https://kotlinlang.org/docs/control-flow.html
  • Kotlin 公式ドキュメント「Ranges and progressions」 https://kotlinlang.org/docs/ranges.html
  • Kotlin 公式ドキュメント「Returns and jumps」 https://kotlinlang.org/docs/returns.html
  • Kotlin 公式ドキュメント「Inline functions」(break と continue) https://kotlinlang.org/docs/inline-functions.html
  • Kotlin 公式ドキュメント「What's new in Kotlin 1.9.0」 https://kotlinlang.org/docs/whatsnew19.html
  • Kotlin 公式ドキュメント「What's new in Kotlin 2.2.0」 https://kotlinlang.org/docs/whatsnew22.html

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Kotlin for文の書き方 until・downTo・step早見表」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?