6.8 KiB
title, description, tags
| title | description | tags | ||
|---|---|---|---|---|
| Циклы и диапазоны | for-in, диапазоны и прогрессии, while, метки и отсутствие классического for |
|
В Kotlin один цикл обхода - for (x in source). Классического for (int i = 0; i < n; i++) нет: его роль выполняют диапазоны.
Диапазоны
for (i in 1..5) print(i) // 12345 — включая 5
for (i in 1..<5) print(i) // 1234 — исключая 5
for (i in 5 downTo 1) print(i) // 54321
for (i in 1..10 step 3) print(i) // 1 4 7 10
for (c in 'a'..'e') print(c) // abcde
| Конструкция | Смысл |
|---|---|
a..b |
от a до b включительно |
a..<b |
от a до b, не включая b |
a until b |
то же, что a..<b (старая форма) |
a downTo b |
по убыванию, включительно |
step n |
с шагом n |
Шаг всегда положительный, даже в downTo: направление задаёт downTo, а не знак шага.
Диапазон - это объект, его можно хранить и проверять:
val valid = 1..100
println(42 in valid) // true
println(valid.first) // 1
println(valid.last) // 100
println((1..10 step 3).last) // 10
Обход по индексам
val list = listOf("a", "b", "c")
for (i in list.indices) print(i) // 012
for (i in 0..<list.size) print(i) // 012 — то же самое
for (item in list) print(item) // abc — предпочтительно
for ((i, item) in list.withIndex()) { // 0:a 1:b 2:c
print("$i:$item ")
}
withIndex() возвращает пары, которые тут же разбираются деструктуризацией (Деструктуризация).
Обход коллекций и Map
val map = mapOf("a" to 1, "b" to 2)
for ((key, value) in map) {
println("$key=$value")
}
for работает с любым типом, у которого есть функция iterator() - включая свои классы.
while и do-while
var i = 0
while (i < 3) {
print(i) // 012
i++
}
do {
val line = readLine()
} while (line != null) // тело выполнится минимум один раз
Переменная, объявленная в do, видна в условии while - такого в Java нет.
break и continue
for (i in 1..10) {
if (i == 3) continue // пропустить итерацию
if (i == 5) break // выйти из цикла
print(i) // 124
}
Метки
Метка - идентификатор с @. Нужна, чтобы выйти из внешнего цикла:
outer@ for (i in 1..3) {
for (j in 1..3) {
if (j == 2) continue@outer // к следующей i
if (i == 3) break@outer // выход из обоих циклов
print("$i$j ") // 11 21
}
}
Циклы - не выражения
for и while возвращают Unit, присвоить их результат нельзя. Если нужен результат обхода - это операции над коллекциями:
val doubled = (1..5).map { it * 2 } // [2, 4, 6, 8, 10]
val evens = (1..10).filter { it % 2 == 0 } // [2, 4, 6, 8, 10]
val sum = (1..100).sum() // 5050
В идиоматичном Kotlin явные циклы встречаются редко. Подробнее: Операции над коллекциями
repeat
Когда индекс не нужен:
repeat(3) { println("повтор") }
repeat(3) { i -> println("итерация $i") } // индекс доступен, с нуля
Это не оператор, а inline-функция стандартной библиотеки.
[!info] Отличия от Java
Java Kotlin for (int i=0; i<n; i++)for (i in 0..<n)for (T x : collection)for (x in collection)label: for (...)outer@ for (...)break label;break@outerтри части в заголовке forтолько inнет диапазонов 1..10,step,downToЧто потеряли: заголовок
forс несколькими переменными и произвольным условием. Если нужна такая логика - пишетсяwhile.Сравнить: Циклы в Java
[!warning] Подводные камни
1..5включает 5. Самая частая ошибка при переходе с Java: индексы массива - это0..<size, а не0..size:val list = listOf("a", "b", "c") // for (i in 0..list.size) println(list[i]) // IndexOutOfBounds на i=3 for (i in 0..<list.size) println(list[i]) // верно for (i in list.indices) println(list[i]) // ещё лучше
downToсоstepтребует положительного шага:for (i in 5 downTo 1 step 2) print(i) // 531 // for (i in 5 downTo 1 step -2) // IllegalArgumentException в рантаймеПустой диапазон не ошибка, просто ноль итераций:
for (i in 5..1) print(i) // ничего не напечатаетМолчаливый баг, если границы перепутаны.
(5..1).isEmpty()-true.Коллекцию нельзя изменять во время обхода:
val list = mutableListOf(1, 2, 3) // for (x in list) if (x == 2) list.remove(x) // ConcurrentModificationException list.removeAll { it == 2 } // правильный способДиапазон
LongиCharесть, аDouble- нет.1.0..2.0создастClosedFloatingPointRange: проверятьinможно, но обойти циклом - нельзя (нетiterator()).
Дополнительно: Операции над коллекциями Далее: Строковые шаблоны и вывод