Streams e datas para os desafios do dia a dia no Java 8

Streams e datas para os desafios do dia a dia no Java 8
alexandre.aquiles
alexandre.aquiles

Compartilhe

Já falamos sobre algumas das novidades do Java 8, como lambdas, streams, default methods e method references e a nova API de data/hora.

Que tal juntar tudo isso pra resolver um probleminha bacana?

Imagine que queremos gerar uma lista com todos dias úteis de um determinado mês. Para o nosso problema, consideramos que dia útil é qualquer dia que não estiver no final de semana, ou seja, sem contar feriados.

Gerando dias úteis da maneira clássica

Banner de divulgação da Imersão IA da Alura em colaboração com o Google. Mergulhe em Inteligência artificial com a Alura e o Google. Serão cinco aulas gratuitas para você aprender a usar IA na prática e desenvolver habilidades essenciais para o mercado de trabalho. Inscreva-se gratuitamente agora!

Recapitulando, a API java.time tem uma classe que representa o período de um dia (LocalDate) e uma classe que representa um mês inteiro (YearMonth).

Para obter o mês de maio de 2014, faríamos: ```java YearMonth anoMes = YearMonth.of(2014, 5);


Poderíamos obter a duração desse mês: ```java
 anoMes.lengthOfMonth(); // 31 

Também seria possível obter um dia específico desse mês: ```java LocalDate data = anoMes.atDay(28); // 28/05/2014


Com o dia em mãos, poderíamos descobrir o dia da semana: ```java
 data.getDayOfWeek(); // DayOfWeeek.WEDNESDAY 

Utilizando os recursos acima, podemos gerar a lista de dias úteis do mês/ano percorrendo com um for do primeiro ao último dia do mês e adicionando a uma lista os dias que não são sábado nem domingo.

Juntando tudo, nossa solução iterativa ficaria assim: ```java int ano = 2014; int mes = 5; YearMonth anoMes = YearMonth.of(ano, mes);

List listaDosDiasUteisDoMes = new ArrayList<>();

//gerando for(int dia=1; dia <= anoMes.lengthOfMonth(); dia++){ LocalDate data = anoMes.atDay(dia);

//filtrando if(!data.getDayOfWeek().equals(DayOfWeek.SATURDAY) && !data.getDayOfWeek().equals(DayOfWeek.SUNDAY)){

//coletando listaDosDiasUteisDoMes.add(data); } }


Observe que **geramos** os dias com o `for` e o método `atDay`.

Depois disso, **filtramos** os dias úteis com a condição de dentro do `if`.

Finalmente, **coletamos** os dias úteis em uma lista com o método `add` de `List`.

## Gerando dias úteis com lambdas e streams

Mas e se utilizarmos os novos recursos do Java 8? Como ficaria nossa solução?

Nosso primeiro passo é, dado um mês, gerar todos os dias possíveis para aquele mês.

A classe `Stream` possui um método chamado `iterate` que, dados um valor inicial e um lambda de incremento, retorna uma sequência infinita de valores, sucessivamente.

Por exemplo, para gerar uma sequência (mais especificamente, uma `Stream`) com todos os números inteiros positivos, poderíamos fazer: ```java
 Stream<Integer> inteirosPositivos = Stream.iterate(1, n -> n + 1); 

Mas como trabalhar com essa sequência infinita? Poderíamos pegar os primeiros 10 inteiros positivos utilizando o método limit da classe Stream: ```java inteirosPositivos.limit(10);


Interessante, não? Refletindo um pouco, podemos dizer que os meses começam no dia 1º e vão sendo incrementados, limitando-se ao número de dias do mês.

Se tivermos uma variável chamada `anoMes` que contém um `YearMonth` representando um mês qualquer, poderíamos gerar todos os dias desse mês com o seguinte código: ```java
 Stream<LocalDate> todosOsDiasDoMes = Stream.iterate(anoMes.atDay(1), data -> data.plusDays(1)) .limit(anoMes.lengthOfMonth()); 

Observe acima que o valor inicial passado para o método iterate foi o dia 1º do mês. Já o lambda de incremento utilizou o método plusDays de LocalDate para adicionar um dia.

Com os dias do mês na mão, podemos utilizar o método filter da classe Stream. Esse método recebe um lambda que encapsula uma condição e cria um novo Stream com apenas os elementos que atendem a condição.

No nosso caso, precisamos garantir que a data não é um sábado nem um domingo: ```java Stream diasUteisDoMes = todosOsDiasDoMes.filter(data -> !data.getDayOfWeek().equals(DayOfWeek.SATURDAY) && !data.getDayOfWeek().equals(DayOfWeek.SUNDAY));


Agora, temos uma `Stream` com apenas os dias úteis do mês. Precisamos coletar o resultado em uma lista. Para isso, utilizaremos o método `collect` de `Stream`, que recebe um `Collector`. Utilizaremos um coletor já existente na classe auxiliar `Collectors`. ```java
 List<LocalDate> listaDosDiasUteisDoMes = diasUteisDoMes.collect(Collectors.toList()); 

Colocando todo o código junto e omitindo variáveis auxiliares, teríamos: ```java int ano = 2014; int mes = 5; YearMonth anoMes = YearMonth.of(ano, mes);

List listaDosDiasUteisDoMes = Stream.iterate(anoMes.atDay(1), data -> data.plusDays(1)) .limit(anoMes.lengthOfMonth()) .filter(data -> !data.getDayOfWeek().equals(DayOfWeek.SATURDAY) && !data.getDayOfWeek().equals(DayOfWeek.SUNDAY)) .collect(Collectors.toList());



Observe que os passos do nosso algortimo (gerar valores, filtrá-los e coletar os resultados) foram facilmente mapeados para métodos da classe `Stream`. Fizemos uma sequência de operações (ou _pipeline_) que foram retornando novas instâncias de `Stream`, até coletarmos os resultados, terminando nossa computação .

Poderíamos [isolar o código acima em uma classe](https://gist.github.com/alexandreaquiles/10300153) e utilizá-la em vários pontos de uma aplicação. Seria interessante expandir a solução para gerar todos os dias úteis de um ano e, quem sabe, ler os feriados de um arquivo ou WebService.

Com lambdas e streams, podemos fazer sequências de operações de maneira natural e elegante. E manipular datas com a API java.time é simples e fácil.

E você? Já está pronto para usar os novos recursos do Java 8 nos seus projetos?

Veja outros artigos sobre Programação