- Kotlin
Kotlin for文の書き方 until・downTo・step早見表
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期連続の黒字決算。