Streams e datas para os desafios do dia a dia no Java 8
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
//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
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
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?