Java 8 Streams: Pare de usar ‘for’ e simplifique seu código!
Tags: java, java8, programacaofuncional, stream
Published: 2019-03-30 22:25:14

---
title: Java 8 Streams: Pare de usar ‘for’ e simplifique seu código!
published: true
tags: Java,Java 8,ProgramacaoFuncional,Stream
canonical_url: https://rinaldo.dev/java-8-streams-pare-de-usar-for-e-simplifique-seu-codigo/
cover_image: https://rinaldo.dev/wp-content/uploads/2019/03/C%C3%B3pia-de-Java-8-Lambda-Expressions-1.png
---

Streams do Java 8 deixam seu código mais **legível** e **conciso**, além de poderem aumentar sua **produtividade** ao lidar com listas e coleções. Aprenda agora e aumente a **qualidade** do seu código!

## Loops tradicionais

Iterar sobre uma `Collection` antes do Java 5 era um código feio e **complexo**, o que sempre aumenta a chance de criar um **bug** inesperado. Lembremos:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 6, 3, 2, 1, 8, 5, 7, 4);
for (Iterator<Integer> numero = lista.iterator(); numero.hasNext();) {
    Integer integer = numero.next();
    System.out.print(integer);
}
```

Felizmente, a partir do Java 5 foi criada a nova sintaxe _For-Each_, que diminuiu muito a complexidade de iterar sobre coleções:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 6, 3, 2, 1, 8, 5, 7, 4);
for (Integer numero : lista) {
    System.out.print(numero);
}
```

Apesar da nova sintaxe ter facilitado muito a vida de desenvolvedores java, ela ainda não tirou proveito da programação funcional e é fortemente **imperativa**. No Java 8 isso mudou **completamente**.

* * *

## Streams do Java 8

O Java 8 trouxe a criação de _funções lambda._ Se você ainda não está familiarizado com elas, [**pode conhecer mais clicando aqui**](https://dev.to/rinaldodev/java-8-entenda-facilmente-funes-lambda-a-principal-novidade-59b). Com isso, foi possível criar uma **nova** forma de iterar sobre coleções, que é mais **simples**, **concisa** e ainda mais **legível**. Veja um exemplo simples, que faz o **mesmo** que os dois exemplos anteriores:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 6, 3, 2, 1, 8, 5, 7, 4);
lista.stream().forEach(System.out::print);
```

Essa sintaxe cria um **stream** de dados. Neste caso, os dados são os números. Para **cada** um deles é chamada a função `System.out.print(...)` passando o número como parâmetro. Aqui também estamos utilizando a sintaxe de _method reference_, que é essa forma de referenciar o método: `System.out::print`.

* * *

## Operações intermediárias em Streams do Java 8

O método `forEach` que utilizamos acima é uma **operação**  **final**, ou **terminal**, pois depois dela nada mais pode ser feito. Mas você pode fazer muito mais com Streams do Java 8. Vamos ver agora exemplos com **operações intermediárias**.

### Os métodos `skip`, `limit` e `distinct`

Vejamos o exemplo abaixo que filtra alguns resultados antes de apresentá-los.

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
lista.stream()
    .skip(2) // ignora os dois primeiros números
    .limit(9) // limita a 9 números
    .distinct() // ignora números iguais
    .forEach(System.out::print);
```

A execução desse código produz a seguinte saída no console:

```java
8742315
```

Nesse exemplo são feitas várias operações **antes** de executar o `forEach:`

- O `skip` serve para ignorar os primeiros X itens. Aqui ignoramos o 1 e o 5.
- O `limit` informa quantos objetos você quer tratar, a partir disso os próximos são ignorados. Aqui pegamos do 8 ao 5.
- O `distinct` é igual ao `DISTINCT` do SQL: ele mantém apenas os resultados que são diferentes entre si. Para isso, ele utiliza o método `equals` dos objetos da lista. Aqui ignoramos a repetição dos números 2 e 8.

### O método `filter`

As 3 funções que mostramos acima “filtram” o nosso stream, ou seja, **ignoram** alguns elementos. Mas e se for necessário um filtro mais **personalizado**? Para isso utiliza-se o método `filter:`

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
lista.stream()
    .filter(e -> e % 2 == 0) // mantém apenas números pares
    .forEach(System.out::print); // imprime todos no console
```

O trecho de código acima produz a seguinte saída no console:

```java
842284
```

Perceba que o método `filter`, nesse caso, está filtrando nosso stream para manter **apenas** números pares. Se você ainda não está familiarizado com as funções lambdas, a **estrutura** é a seguinte:

![Java 8: estrutura da função lambda. Primeiro temos os parâmetros da função, depois o arrow token, depois do corpo da função.](https://rinaldo.dev/wp-content/uploads/2019/03/explicacao_lambda-1.png)<figcaption>Estrutura da função lambda passada para o método <code>filter</code></figcaption>

### O método `map`

Caso seja necessário fazer uma **transformação** no dado antes de passá-lo para o próximo método do stream, utiliza-se o método `map`. Apesar do nome, ele não tem relação com a interface `Map`. Veja abaixo.

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
lista.stream()
    .map(e -> e * 2) // multiplica cada item por 2
    .forEach(e -> System.out.print(e + " ")); // imprime todos no console
```

A saída no console é a seguinte:

```java
2 10 16 14 8 4 6 4 2 16 10 14 8
```

Perceba que todos os números foram multiplicados por 2 antes de serem apresentados no console. Porém, é importante notar que **os números da lista original não foram alterados**. Ou seja, as transformações do método `map` afetam apenas os valores que serão passados para frente **naquele** stream. Isso é excelente, pois sempre que possível o ideal é trabalharmos com valores e instâncias **imutáveis**.

* * *

## Operações finais em Streams do Java 8

Em todos os exemplos apresentados até agora o stream finaliza com um `forEach`. Essa é uma das operações **finais** disponíveis na API de Streams do Java 8. Ou seja, é um método que fecha o stream e coleta o resultado de tudo que foi feito. Veremos agora outras operações **finais** que geralmente são mais úteis do que o `forEach`. Afinal, você provavelmente irá querer armazenar o resultado das operações do stream, e utilizar esse resultado para alguma coisa. Vejamos algumas possibilidades.

### Os métodos `max`, `min`, `count`

Se deseja pegar o **maior** ou o **menor** valor depois das operações realizadas no seu stream, os métodos `max` e `min` fazem exatamente isso. Vejamos o `max`:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
Optional<Integer> maiorNumero = lista.stream()
        .map(e -> e * 2) // multiplica cada item por 2
        .max(Comparator.naturalOrder()); // pega o maior item pela ordem natural
System.out.println(maiorNumero.get());
```

Ao utilizar o método `max` é necessário informar como o stream irá **comparar** seus objetos. Nesse caso, como `Integer` já implementa a interface `Comparable` e o método `compareTo`, ele já possui uma **ordem natural** que podemos utilizar, por isso usamos `Comparator.naturalOrder()`. Nesse exemplo o número `16` é impresso no console, pois é o **maior** número no stream depois de termos multiplicado todos por 2. Veja que, por ser uma operação **final**, é necessário armazenar o resultado em uma **variável** para utilizá-lo. O mesmo ocorre com o método `min`:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
Optional<Integer> menorNumero = lista.stream()
        .map(e -> e * 2) // multiplica cada item por 2
        .min(Comparator.naturalOrder()); // pega o menor item pela ordem natural
System.out.println(menorNumero.get());
```

Seguindo a mesma ideia do método `max`, esse exemplo produz a saída `2` no console.

Por fim, o método `count` retorna **quantos** elementos restam no stream:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
Long quantidade = lista.stream()
        .filter(e -> e % 2 == 0) // mantém apenas números pares
        .count(); // pega quantos itens restam no stream
System.out.println(quantidade);
```

Nesse exemplo a saída no console será `6`, que é a quantidade de números pares no stream.

### O método `collect`

E finalizando os exemplos das operações básicas, temos o método `collect`. Ele é a forma mais **personalizável** de coletar o resultado das operações do Stream. Por ter **inúmeras** formas de ser utilizado, falarei mais sobre ele em um artigo específico. Para saber assim que eu **compartilhar**, é só me seguir, os links estão no **final** deste artigo. Por enquanto vejamos alguns exemplos básicos com collect.

Se você quiser apenas coletar o resultado em uma **nova lista**, utilize um collector **pré-implementado** em `Collectors.toList()`:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
List<Integer> novaLista = lista.stream()
        .filter(e -> e % 2 == 0) // mantém apenas números pares
        .map(e -> e * 2) // multiplica cada item por 2
        .collect(Collectors.toList()); // coleta todos os itens em uma nova lista
System.out.println(novaLista);
```

Nesse exemplo **filtramos** apenas os números pares, **multiplicamos** todos por 2 e **coletamos** o resultado em uma nova lista. A saída no console é:

```java
[16, 8, 4, 4, 16, 8]
```

Caso queira **agrupar** os itens da lista em um **mapa**, utilize o collector `Collectors.groupingBy(...)`:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
Map<Boolean, List<Integer>> mapa = lista.stream()
        .map(e -> e * 2) // multiplica cada item por 2
        .collect(Collectors.groupingBy(e -> e > 8)); // agrupa itens baseado no resultado da comparação
System.out.println(mapa);
```

Neste exemplo estamos **multiplicando** todos os itens do stream por 2, e depois **agrupamos** em números **maiores** que 8 e **menores ou igual** a 8. Veja como fica o **mapa** ao ser impresso no console:

```java
{false=[2, 8, 4, 6, 4, 2, 8], true=[10, 16, 14, 16, 10, 14]}
```

E caso queira gerar uma **única string** a partir do stream, pode utilizar o collector `Collectors.joining(...)`, que **une** várias strings em **uma** só:

```java
List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 2, 3, 2, 1, 8, 5, 7, 4);
String stringUnica = lista.stream()
        .map(String::valueOf) // transforma cada item em String
        .collect(Collectors.joining(";")); // junta todas as Strings em uma única separada por ';'
System.out.println(stringUnica);
```

Aqui **transformamos** cada número em uma string utilizando String.valueOf(…). Depois **agrupamos** todos em uma única string separando os elementos por **ponto e vírgula**. A saída no console é a seguinte string:

```java
1;5;8;7;4;2;3;2;1;8;5;7;4
```

* * *

## Parando de usar ‘for’ e ‘while’

Utilizando Streams do Java 8, raramente será necessário que você faça um **loop explícito** novamente, como `for`, `while` ou `do...while`. A API de streams utiliza **loops implícitos**, tira proveito da **programação funcional**, e deixa seu código muito mais **legível** e **conciso**. Além disso, em um próximo artigo mostrarei como você pode **paralelizar** a execução de um stream de forma muito **simples**!

Apesar de termos apresentado streams apenas a partir de **listas**, é possível criá-los de **várias** outras formas! Veja:

- De um **array** utilizando `Arrays.stream(Object[])`;
- De **métodos estáticos** em classes de **stream**, como `Stream.of(Object[])`, `IntStream.range(int, int)`;
- As **linhas** de um **arquivo** podem ser obtidas com `BufferedReader.lines()`;
- Streams de **arquivos** podem ser obtidos através de métodos na classe `Files`;
- Streams de **números aleatórios** podem ser obtidos em `Random.ints()`.

**Parabéns**! Agora **você** conhece melhor a API de **Streams do Java 8**! Você pode utilizar esse conhecimento para **entregar**  **no prazo** soluções de **alta qualidade**.

Quer entender um pouco mais sobre funções **lambda** e sua **estrutura**? [Veja esse artigo para entender **facilmente** a estrutura de funções lambda.](https://dev.to/rinaldodev/java-8-entenda-facilmente-funes-lambda-a-principal-novidade-59b)

Quer receber minhas **melhores dicas** para escrever código de **alta qualidade** e entregar projetos no prazo? [**Então acesse aqui**.](https://rinaldo.dev/contato/)

Quer aprender a melhorar seu código diariamente? Então me segue no **twitter** : [**https://twitter.com/rinaldodev**](https://twitter.com/rinaldodev)

**E você?** Já teve que dar manutenção em algum código com **streams**? Lembra de situações em que **você** poderia ter utilizado streams? Deixe um **comentário**!

Gostou do que aprendeu? Compartilhe com outros desenvolvedores no WhatsApp e nas suas redes sociais!

O post [Java 8 Streams: Pare de usar ‘for’ e simplifique seu código!](https://rinaldo.dev/java-8-streams-pare-de-usar-for-e-simplifique-seu-codigo/) apareceu primeiro em [rinaldo.dev](https://rinaldo.dev).