Neste tutorial, você vai aprender a trabalhar com todas as estruturas de repetição disponíveis em Kotlin: for, while e do-while. Vamos explorar ranges, iteração sobre coleções, controle de fluxo com break e continue, e padrões idiomáticos que tornam seus loops mais expressivos e seguros.
O Loop For em Kotlin
O loop for em Kotlin é diferente do for tradicional de Java ou C. Ele não usa a sintaxe de três partes (inicialização; condição; incremento). Em vez disso, Kotlin usa uma abordagem mais moderna baseada em iteradores, semelhante ao for-each de outras linguagens.
A forma mais comum do for em Kotlin itera sobre um range (intervalo) ou sobre uma coleção. Essa abordagem é mais segura porque elimina erros comuns como off-by-one e índices fora dos limites.
fun main() {
// For com range (intervalo inclusivo)
println("=== Contando de 1 a 10 ===")
for (i in 1..10) {
print("$i ")
}
println()
// For com range exclusivo (until / ..<)
println("\n=== Índices de 0 a 4 (exclusivo no final) ===")
for (i in 0 until 5) {
print("$i ")
}
println()
// For com passo (step)
println("\n=== Números pares de 2 a 20 ===")
for (i in 2..20 step 2) {
print("$i ")
}
println()
// For decrescente (downTo)
println("\n=== Contagem regressiva ===")
for (i in 10 downTo 1) {
print("$i ")
}
println("\nFogo!")
// For decrescente com passo
println("\n=== De 100 a 0, de 10 em 10 ===")
for (i in 100 downTo 0 step 10) {
print("$i ")
}
println()
}
Os ranges são uma funcionalidade poderosa do Kotlin. O operador .. cria um range inclusivo (ambos os limites são incluídos), enquanto until ou ..< cria um range exclusivo no final (útil para trabalhar com índices de arrays). O downTo permite iteração decrescente, e step define o incremento entre os valores.
Iterando sobre Coleções
Na prática do dia a dia, você vai usar for mais frequentemente para iterar sobre listas, arrays e outras coleções do que sobre ranges numéricos. Kotlin oferece várias formas elegantes de fazer isso.
fun main() {
val frutas = listOf("Maçã", "Banana", "Laranja", "Manga", "Uva")
// Iteração simples sobre lista
println("=== Frutas ===")
for (fruta in frutas) {
println(" - $fruta")
}
// Iteração com índice usando withIndex()
println("\n=== Frutas com índice ===")
for ((indice, fruta) in frutas.withIndex()) {
println(" $indice: $fruta")
}
// Iteração sobre Map (chave-valor)
val capitais = mapOf(
"SP" to "São Paulo",
"RJ" to "Rio de Janeiro",
"MG" to "Belo Horizonte",
"BA" to "Salvador",
"RS" to "Porto Alegre"
)
println("\n=== Capitais ===")
for ((sigla, capital) in capitais) {
println(" $sigla -> $capital")
}
// Iteração sobre String (caracteres)
val palavra = "KOTLIN"
println("\n=== Letras de '$palavra' ===")
for (letra in palavra) {
print("[$letra] ")
}
println()
// Iteração sobre array com índices
val numeros = intArrayOf(10, 20, 30, 40, 50)
println("\n=== Array com indices ===")
for (i in numeros.indices) {
println(" numeros[$i] = ${numeros[i]}")
}
}
A desestruturação com (indice, valor) no withIndex() e (chave, valor) nos maps é uma sintaxe muito prática que torna o código mais legível. Ela funciona com qualquer classe que declare os operadores component1(), component2(), etc., como as data classes.
While e Do-While
Os loops while e do-while funcionam de maneira semelhante a outras linguagens. Use while quando não souber antecipadamente quantas iterações serão necessárias e do-while quando precisar garantir que o bloco execute pelo menos uma vez.
fun main() {
// While básico
println("=== Sequência de Fibonacci até 100 ===")
var a = 0
var b = 1
while (a <= 100) {
print("$a ")
val temp = a + b
a = b
b = temp
}
println()
// Do-while — executa pelo menos uma vez
println("\n=== Adivinhe o número (simulação) ===")
val numeroSecreto = 7
var tentativa = 0
var palpite: Int
do {
tentativa++
palpite = tentativa * 2 + 1 // simulando palpites
println("Tentativa $tentativa: palpite = $palpite")
} while (palpite != numeroSecreto)
println("Acertou em $tentativa tentativas!")
// While para processar dados
println("\n=== Processando fila ===")
val fila = mutableListOf("Pedido-001", "Pedido-002", "Pedido-003", "Pedido-004")
while (fila.isNotEmpty()) {
val pedido = fila.removeFirst()
println("Processando: $pedido (restam ${fila.size})")
}
println("Fila processada!")
}
A diferença fundamental entre while e do-while é que o do-while verifica a condição após executar o bloco, garantindo pelo menos uma execução. Isso é útil em cenários como validação de entrada do usuário, onde você precisa pedir o valor antes de verificar se ele é válido.
Controle de Fluxo: break, continue e Labels
Kotlin oferece as instruções break e continue para controlar o fluxo dentro de loops. Além disso, o sistema de labels permite direcionar essas instruções para loops específicos em estruturas aninhadas, evitando ambiguidade.
fun main() {
// break — interrompe o loop
println("=== Procurando primeiro múltiplo de 7 ===")
for (i in 1..100) {
if (i % 7 == 0) {
println("Encontrado: $i")
break
}
}
// continue — pula para próxima iteração
println("\n=== Números não divisíveis por 3 (de 1 a 15) ===")
for (i in 1..15) {
if (i % 3 == 0) continue
print("$i ")
}
println()
// Labels para loops aninhados
println("\n=== Busca em matriz com label ===")
val matriz = arrayOf(
intArrayOf(1, 2, 3),
intArrayOf(4, 5, 6),
intArrayOf(7, 8, 9)
)
val alvo = 5
busca@ for (i in matriz.indices) {
for (j in matriz[i].indices) {
if (matriz[i][j] == alvo) {
println("Valor $alvo encontrado na posição [$i][$j]")
break@busca // sai de AMBOS os loops
}
}
}
// continue com label
println("\n=== Pares de números (sem repetição do primeiro) ===")
externo@ for (i in 1..3) {
for (j in 1..3) {
if (i == j) continue@externo
println(" ($i, $j)")
}
}
}
Os labels são nomeados com o sufixo @ e podem ser aplicados a qualquer loop. break@label interrompe o loop marcado, enquanto continue@label pula para a próxima iteração do loop marcado. Use labels com moderação — código com muitos labels pode se tornar difícil de ler. Considere extrair a lógica para uma função separada como alternativa.
Alternativas Funcionais aos Loops
Kotlin oferece uma rica biblioteca de funções de alta ordem que frequentemente substituem loops tradicionais com código mais expressivo e conciso. Embora loops explícitos sejam perfeitamente válidos, conhecer essas alternativas é importante para escrever código idiomático.
As funções forEach, map, filter, reduce e muitas outras estão disponíveis em todas as coleções do Kotlin. Elas serão exploradas em detalhes nos tutoriais sobre Listas, Sets e Maps e Lambdas e Higher-Order Functions, mas aqui vai uma amostra para que você saiba que essas alternativas existem.
Dicas e Erros Comuns
Ao trabalhar com loops em Kotlin, esteja atento a estes pontos:
Usar range inclusivo quando deveria ser exclusivo:
for (i in 0..lista.size)vai causarIndexOutOfBoundsExceptionporque incluilista.size. Use0 until lista.sizeou melhor ainda,lista.indices.Tentar modificar uma coleção durante iteração com
for: isso lançaConcurrentModificationException. Useiterator.remove()ou crie uma nova coleção filtrada.Loops infinitos acidentais com
while: sempre garanta que a condição dowhilevai eventualmente se tornar falsa. Umwhile (true)sembreakadequado trava o programa.Não usar
indicesouwithIndex(): em vez defor (i in 0 until lista.size), prefirafor (i in lista.indices)oufor ((i, v) in lista.withIndex()). É mais idiomático e seguro.Abusar de
breakecontinuecom labels: se o código tem muitos labels, provavelmente deveria ser refatorado. Extraia a lógica para funções menores e mais claras.Usar loops onde funções de coleção seriam mais claras: para transformações simples como filtrar ou mapear, as extension functions de coleção geralmente produzem código mais legível que loops manuais.
Conclusão e Próximos Passos
Neste tutorial, você dominou todas as estruturas de repetição do Kotlin. O loop for com ranges e coleções, while e do-while para iterações condicionais, e os mecanismos de controle de fluxo com break, continue e labels. Esses são ferramentas essenciais que você vai usar em praticamente todo programa que escrever.
Para continuar evoluindo, siga para estes tutoriais:
- Funções em Kotlin — aprenda a criar funções reutilizáveis
- Listas, Sets e Maps — domine as coleções e suas operações
- Lambdas e Higher-Order Functions — descubra alternativas funcionais aos loops
Pratique criando programas que combinem loops com as estruturas condicionais que aprendemos anteriormente. Bons exercícios incluem tabuada, sequência de Fibonacci, números primos e ordenação de listas. Até o próximo tutorial!