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

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