DistinctBy na API Java Stream

DistinctBy na API Java Stream

1. Visão geral

Procurar elementos diferentes em uma lista é uma das tarefas comuns que nós, como programadores, geralmente enfrentamos. A partir do Java 8 com a inclusão deStreams, temos uma nova API para processar dados usando abordagem funcional.

Neste artigo, mostraremos diferentes alternativas para filtrar uma coleção usando um atributo específico de objetos na lista.

2. Usando a API de Stream

A API Stream fornece o métododistinct() que retorna diferentes elementos de uma lista com base no métodoequals() da classeObject.

No entanto, torna-se menos flexível se quisermos filtrar por um atributo específico. Uma das alternativas que temos é escrever um filtro que mantenha o estado.

2.1. Usando um Filtro Stateful

Uma das soluções possíveis seria implementar umPredicate: com estado

public static  Predicate distinctByKey(
    Function keyExtractor) {

    Map seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

Para testá-lo, usaremos a seguinte classePerson que tem os atributosage,email ename:

public class Person {
    private int age;
    private String name;
    private String email;
    // standard getters and setters
}

E para obter uma nova coleção filtrada porname, podemos usar:

List personListFiltered = personList.stream()
  .filter(distinctByKey(p -> p.getName()))
  .collect(Collectors.toList());

3. Usando coleções Eclipse

Eclipse Collections é uma biblioteca que fornece métodos adicionais para processarStreamse coleções em Java.

3.1. Usando oListIterate.distinct()

O métodoListIterate.distinct() nos permite filtrar umStream usando váriosHashingStrategies.. Essas estratégias podem ser definidas usando expressões lambda ou referências de método.

Se quisermos filtrar pelo nomePerson’s:

List personListFiltered = ListIterate
  .distinct(personList, HashingStrategies.fromFunction(Person::getName));

Ou, se o atributo que vamos usar for primitivo (int, long, double), podemos usar uma função especializada como esta:

List personListFiltered = ListIterate.distinct(
  personList, HashingStrategies.fromIntFunction(Person::getAge));

3.2. Dependência do Maven

Precisamos adicionar as seguintes dependências ao nossopom.xml para usar coleções Eclipse em nosso projeto:


    org.eclipse.collections
    eclipse-collections
    8.2.0

Você pode encontrar a versão mais recente da biblioteca Eclipse Collections no repositórioMaven Central.

Para aprender mais sobre esta biblioteca, podemos ir parathis article.

4. Using Vavr (Javaslang *) *

Esta é uma biblioteca funcional para Java 8 que fornece dados imutáveis ​​e estruturas de controle funcional.

4.1. UsandoList.distinctBy

Para filtrar listas, esta classe fornece sua própria classe List que possui o métododistinctBy() que nos permite filtrar por atributos dos objetos que ela contém:

List personListFiltered = List.ofAll(personList)
  .distinctBy(Person::getName)
  .toJavaList();

4.2. Dependência do Maven

Adicionaremos as seguintes dependências ao nossopom.xml para usar o Vavr em nosso projeto.


    io.vavr
    vavr
    0.9.0

Você pode encontrar a versão mais recente da biblioteca Vavr no repositórioMaven Central.

Para aprender mais sobre esta biblioteca, podemos ir parathis article.

5. Usando StreamEx

Esta biblioteca fornece classes e métodos úteis para o processamento de fluxos Java 8.

5.1. UsandoStreamEx.distinct

Dentro das classes fornecidas estáStreamEx que tem o métododistinct para o qual podemos enviar uma referência ao atributo onde queremos distinguir:

List personListFiltered = StreamEx.of(personList)
  .distinct(Person::getName)
  .toList();

5.2. Dependência do Maven

Adicionaremos as seguintes dependências ao nossopom.xml para usar o StreamEx em nosso projeto.


    one.util
    streamex
    0.6.5

Você pode encontrar a versão mais recente da biblioteca StreamEx no repositórioMaven Central.

6. Conclusão

Neste tutorial rápido, exploramos exemplos de como obter elementos diferentes de um Stream, com base em um atributo usando a API Java 8 padrão e alternativas adicionais com outras bibliotecas.

Como sempre, o código completo está disponívelover on GitHub.