O que é?

A função forEach permite percorrer uma lista, realizando alguma ação em seus elementos.

Por que aprender sobre isso?

Listas estão entre as estruturas de dados mais utilizadas em uma aplicação. Uma fatura pode ter uma lista de produtos, por exemplo. Dessa forma, lidar com listas deve ser trivial e forEach é uma maneira concisa para isso.

Características

  • É escrito forEach, com "E" maiúsculo;
  • É uma função presente em diversas coleções;
  • Dentro do forEach, o item atual é referenciado como it.

O que saber antes de começar?

A sintaxe do forEach pode ser vista no Código 1.


fun main(args: Array<String>) {
      val lista = listOf("Kotlin", "Android")
   
      lista.forEach {
          println(it)
      }
}
Código 1. Sintaxe do forEach

Note que usamos chaves no forEach e não parênteses como em outras linguagens.

A variável it representa, dentro do bloco forEach, o elemento da lista que está sendo iterado.

O resultado da execução desse código é apresentado na Figura 1.

Resultado do código forEach impresso no terminal
Figura 1. Resultado do código forEach impresso no terminal

forEach na prática

Vejamos a seguir alguns exemplos práticos que introduzem conceitos importantes sobre o método forEach.

Exemplo 1 - Comparando for e forEach

No Código 2 comparamos for e forEach.

val lista = listOf("Kotlin", "Android")

for(item in lista) {
    println(item)
}

lista.forEach {
    println(it)
}
Código 2. Comparando for e forEach

Note como forEach é mais conciso em relação ao for.

Exemplo 2 - Variável it

A variável it não pode ser modificada dentro de um forEach, pois é um valor (val).

Veja no Código 3 a seguir um exemplo que tenta alterar o valor de it.

val lista = listOf("Kotlin", "Android")

lista.forEach {
    println(it)
    it = "Não permitido"
}
Código 3. Tentando alterar a varável it

Ao tentar alterar o valor de it dessa forma será lançado o erro Val cannot be reassigned como pode ser visto na Figura 2.

Erro gerado ao tentar alterar a variável it
Figura 2. Erro gerado ao tentar alterar a variável it

Exemplo 3 - Utilizando return

Caso seja necessário pular um item da lista durante o processamento, podemos usar return@forEach. Veja um exemplo no Código 4.

val lista = listOf("Kotlin", "Android")

lista.forEach {
    if(it.startsWith("K")) {
        return@forEach
    }

    println(it)
}
Código 4. Utilizando return para pular um item da lista

Nesse código it.startsWith("K") verifica se o item da lista começa com a letra K e se esse for o caso return@forEach salta o processamento para o próximo item.

Como resultado, apenas a palavra "Android" é apresentada no console como pode ser visto na Figura 3.

Resultado impresso no console
Figura 3. Resultado impresso no console

Exemplo 4 - Interagindo com objetos

Neste exemplo, percorremos uma lista de objetos exibindo as suas propriedades.

O objeto em questão é uma instância da classe Produto do Código 5.

data class Produto(val code: String, val valor: Double)
Código 5. Classe Produto

O código para processar essa lista ficará como mostra o Código 6.

val lista = listOf(Produto("Kotlin101", 14.99), Produto("Android101", 19.99))

lista.forEach {
    println("${it.code} - ${it.valor}")
}
Código 6. Processando um objeto

Dentro do bloco forEach usamos it para acessar as propriedades do objeto, como faríamos com qualquer outra instância.

Como resultado teremos o código e valor de cada produto na lista apresentado no console como exibe a Figura 4.

Código e valor de cada produto impresso no
console
Figura 4. Código e valor de cada produto impresso no console

Exemplo 5 - Listas de listas

Para processar listas de listas podemos usar blocos forEach encadeados.

Por exemplo, suponha que tenhamos uma lista de dois itens, os quais são listas de objetos com mostra o Código 7.


val lista = listOf(
      listOf(Produto("Kotlin101", 14.99), Produto("Android101", 19.99)),
      listOf(Produto("Java101", 29.99), Produto("Spring101", 59.99))
)
Código 7. Exemplo de uma lista de listas

É possível processar essa lista usando a função forEach da lista e depois em cada produto dentro dela como mostra o Código 8.


lista.forEach {
      it.forEach{
          println("${it.code} - ${it.valor}")
      }
}
Código 8. Processando lista de listas

Como resultado teremos o código e valor de cada produto na lista apresentado no console como mostra a Figura 5.

Imprimindo a lista de listas
Figura 5. Imprimindo a lista de listas

Exemplo 6 - Alterando o nome da variável it

Ao lidar com blocos forEach encadeados, cuidado para não se perder quanto ao valor de it.

Por exemplo, revisitando o código anterior, tanto lista.forEach quanto it.forEach possuem uma variável interna chamada it, como mostra o Código 9.


lista.forEach { // it aqui
      it.forEach{ // it aqui
          println("${it.code} - ${it.valor}")
      }
}
Código 9. Percorrendo uma lista de listas

A diferença é que dentro de lista.forEach it será uma lista de objetos, como vemos no Código 10.


val lista = listOf(
      listOf(Produto("Kotlin101", 14.99), Produto("Android101", 19.99)), // it de lista.forEach
      ...
)
Código 10. Lista representada pela variável it

Já em it.forEach it é cada objeto na lista, os quais são processados um a um, como mostra o Código 11.


val lista = listOf(
      listOf(
          Produto("Kotlin101", 14.99), // it de it.forEach
          Produto("Android101", 19.99), // it de it.forEach
      ),
      ...
)
Código 11. Lista que está sendo iterada

Para evitar confusões como qual it estamos usando, podemos mudar o nome dessa variável, atribuindo a ela melhor sentido em um dado contexto, como mostra o Código 12.


lista.forEach { // it continua com o mesmo nome aqui
      it.forEach{ produto -> // it é renomeado para produto aqui
          println("${produto.code} - ${produto.valor}") // produto é acessado aqui
      }
}
Código 12. Renomeando a variável it

Dessa forma removemos a ambiguidade, ao usarmos ${produto.code} - ${produto.valor} sabemos que estamos usando o objeto na lista.

Exemplo 8 - Alterando os valores das propriedades de um objeto

Dentro de um forEach podemos mudar o valor das propriedades de um objeto. Para isso vamos modificar a classe Produto para que, em lugar de val, valor seja declarado como var Veja o antes (Código 13) e o depois (Código 14).

data class Produto(val code: String, val valor: Double)
Código 13. Classe Produto antes de ser modificada
data class Produto(val code: String, var valor: Double)
Código 14. Classe Produto depois de ser modificada

Agora que a propriedade valor pode ser modificada, basta realizar essa operação dentro do forEach, como apresentado no Código 15.

lista.forEach {
      it.forEach{ produto ->
          produto.valor += 1.0
          println("${produto.code} - ${produto.valor}")
      }
}
Código 15. Alterando o valor da propriedade valor

Como resultado veremos na Figura 6, serem exibidos os valores de cada instância de Produto acrescido em 1.

Exibindo os valores de cada instância de Produto
acrescido em 1
Figura 6. Exibindo os valores de cada instância de Produto acrescido em 1

Quando não utilizar forEach

Em alguns casos, usar for pode ser melhor que forEach. Vejamos alguns exemplos.

Exemplo 1 - Quando precisar utilizar continue

Cuidado, caso use continue dentro da função forEach o erro "'break' and 'continue' are only allowed inside a loop" será lançado. Lembre-se, forEach não é um laço de repetição como for, mas uma função das coleções.

O Código 16 gera o erro mencionado.

lista.forEach {
      if(it.startsWith("K")) {
          continue
      }
   
      println(it)
}
Código 16. Código que gera o erro "'break' and 'continue' are only allowed inside a loop"

Como resultado da execução dele teremos a Figura 7.

Exibindo os valores de cada instância de Produto
acrescido em 1
Figura 7. Exemplo de erro causado pelo uso da palavra continue dentro da função forEach

No caso de continue ser necessário, talvez seja melhor usar for em lugar de forEach.

O Código 16 equivalente a esse acima, mas utilizando for em lugar de forEach pode ser visto no Código 17.

for(item in lista){
      if(item.startsWith("K")) {
          continue
      }
   
      println(item)
}
Código 17. Utilizando for no lugar de forEach

Note que em lugar de it, com for usamos uma variável nomeada, nesse caso, de item.

Exemplo 2 - Quando precisar utilizar break

Não existe o equivalente a break dentro da função forEach, visto que ela não é um laço de repetição. Em lugar disso, podemos usar return para encerrar a função, como mostra o Código 18.

lista.forEach {
      if(it.startsWith("K")) {
          return
      }
   
      println(it)
}
Código 18. Utilizando return em vez de break

Como resultado nenhuma palavra será impressa, uma vez que a função será encerrada no processamento do primeiro item. Veja na Figura 8 que nada foi impresso.

Função encerrada ao utilizar return e nada foi
impresso no Console
Figura 8. Função encerrada ao utilizar return e nada foi impresso no Console

No caso de break ser necessário talvez seja melhor usar for em lugar de forEach.

O Código 18 equivalente a esse acima, mas utilizando for em lugar de forEach pode ser visto no Código 19.

for(item in lista) {
      if(item.startsWith("K")) {
          break
      }
   
      println(item)
}
Código 19. Utilizando for no lugar de forEach

Novamente, note que em lugar de it usamos nesse exemplo uma variável chamada, nesse caso, de item.

Conclusão

No geral, a performance de forEach pode ser melhor que for. forEach é ainda preferível para processamento de objetos, quando operações como break ou continue não são necessárias. É comum ainda usarmos forEach de forma encadeada, após filter, ou outra função semelhante.