Executando verificação de segurança...
1

Funcionalidades do JDK 8 (Java 8) - Parte VI - API de Streams

API de Streams - Parte II

Famílias de streams

De uma olhada no código a seguir:

Stream<Integer> stream = pilotos.stream().map(Piloto::getPontuacao);

O problema nesse código é que ele gera boxing dos valores inteiros, o que gera um overhead indesejado se formos operar esses dados, em caso de listas grandes isso pode causar diversos problemas e até parar a aplicação dependendo do caso.

A boa notícia é que o pacote java.util.stream tem diversas implementações equivalentes ao stream para os principais tipos como IntStream, LongStream, e DoubleStream.

Nesse caso podemos utilizar o IntStream a fim de evitar o autoboxing, para isso devemos utilizar no lugar do map() o mapToInt(), veja só:

IntStream stream = pilotos.stream().mapToInt(Piloto::getPontuacao);

Note que mapToInt() recebe uma função mais específica chamada ToIntFunction

IntStream mapToInt(ToIntFunction<? super T> mapper);

Outra coisa legal é que o IntStream possui métodos específicos que podem simplificar bastante o trabalho com inteiros, como max(), sorted() e average().

Por exemplo para obter a pontuação média podemos invovar o método average()

double mediaDaPontuacao = pilotos.stream()
        .mapToInt(Piloto::getPontuacao)
        .average()
        .getAsDouble();

Optional

Note que utilizamos o método getAsDouble(), se você foi curioso o bastante pra olhar a implementação desse método viu que ele, antes de retornar o valor faz alguma coisa:

public double getAsDouble() {
    if (!isPresent) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

Essa validação do if tira proveito de uma funcionalidade que chamamos de optional e serve para que não seja necessário fazer validações extremas, como por exemplo, se preocupar se o número de pilotos for zero. A classe java.util.Optional permite que possamos cuidar desses casos de uma maneira simples.

Além disso há também outras versões primitivas como OptionalDouble, OptionalInt. O método average() do IntStream, por exemplo, devolve um OptionalDouble

OptionalDouble average();
OptionalDouble media = pilotos.stream()
        .mapToInt(Piloto::getPontuacao)
        .average();

Assim sendo podemos utilizar uma interface fluente para cuidarmos de casos como uma lista vazia, podendo implementar dessa forma:

double mediaDaPontuacao = pilotos.stream()
        .mapToInt(Piloto::getPontuacao)
        .average()
        .orElse(0.0);

Nesse caso se a lista estiver vazia fica valendo o valor do método orElse() ou seja 0.0. como resultado pra média. Caso haja necessidade de lançar uma exceção, podemos utilizar o orElseThrow(), da seguinte forma:

double mediaDaPontuacao = pilotos.stream()
        .mapToInt(Piloto::getPontuacao)
        .average()
        .orElseThrow(IllegalStateException::new);

Ainda é possível verificar se dentro do Optional existe um valor e caso exista passamos um consumer, algo mais ou menos assim:

pilotos.stream()
        .mapToInt(Piloto::getPontuacao)
        .average()
        .ifPresent(value -> System.out.println(value));

Como o método ifPresent() recebe um DoubleConsumer e ele é uma interface funcional, logo podemos utilziar method references aqui:

pilotos.stream()
        .mapToInt(Piloto::getPontuacao)
        .average()
        .ifPresent(System.out::println);

O principal benefício do Optional é que dessa forma sempre evitamos um NullPointerException.

Em um exemplo prático, digamos que queremos o piloto com maior pontuação, para isso já vimos que podemos utilizar o método max() e esse método recebe um Comparator, veja a assinatura:

Optional<T> max(Comparator<? super T> comparator);

Assim poderíamos ter a seguinte implementação:

Optional<Piloto> vencedor = pilotos.stream()
        .max(Comparator.comparingInt(Piloto::getPontuacao));

Nesse caso se a lista for vazia não haverá um piloto para ser retornado, e o resultado será um Optional vazio, dito isso, você deve verificar se há ou não um piloto presente no resultado, para essa finalidade você pode utilizar o método get(), podendo receber um null ou algo mais elaborado e seguro como orElse() ou ifPresent().

Podemos inclusive trabalhar de forma lazy com o Optional, digamos que você queira o nome do usuário com mais pontos, sendo assim você pode fazer algo da seguinte forma:

Optional<String> vencedor = pilotos.stream()
        .max(Comparator.comparingInt(Piloto::getPontuacao))
        .map(piloto -> piloto.getNome());

Sabendo que map() recebe uma Function conforme a assinatura abaixo, podemos ser ainda mais resumidos

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

E escrever o mesmo código para obter o nome do usuário com mais pontos utilizar method reference

Optional<String> vencedor = pilotos.stream()
        .max(Comparator.comparingInt(Piloto::getPontuacao))
        .map(Piloto::getNome);

Agora que conhecemos um pouco sobre a API de streams, vamos no próximo artigo saber quais operações são possíveis de serem feitas com essas estruturas.

Carregando publicação patrocinada...