Distinto por propiedad en Java 8 Stream

Sheeraz Gul 12 octubre 2023
  1. Distinto por propiedad en Java 8 Stream
  2. Use Collectors.toMap para aplicar Distinct by Property en Java
  3. Use una clase contenedora para aplicar Distinct by Property en Java
  4. Use el método distinctByKey para aplicar Distinct by Property en Java
Distinto por propiedad en Java 8 Stream

Este tutorial demuestra la funcionalidad distintiva por propiedad usando la secuencia en Java.

Distinto por propiedad en Java 8 Stream

Java 8 Stream tiene un método distinct() que filtra los duplicados de una lista. Este método utiliza el método equal de la instancia para verificar los elementos únicos y, si ve duplicados, eliminarlos.

No podemos usar el método distinct() si se requiere aplicarlo en una determinada propiedad o campo. Pero podemos aplicar el método distinct() por propiedad usando una forma particular.

Probemos un ejemplo en el que creamos una clase Empleado que tiene el nombre y la identificación del empleado y luego tratamos de usar el método distinct() en la secuencia en la lista de clases Empleado:

package delftstack;

import java.util.List;
import java.util.Objects;

class Employee {
  private String Employee_Name;
  private int Employee_ID;

  public Employee(String Employee_Name, int Employee_ID) {
    this.Employee_Name = Employee_Name;
    this.Employee_ID = Employee_ID;
  }

  public String getName() {
    return Employee_Name;
  }

  public int getID() {
    return Employee_ID;
  }

  @Override
  public boolean equals(Object Demo_Object) {
    if (this == Demo_Object) {
      return true;
    }
    if (Demo_Object == null) {
      return false;
    }
    if (getClass() != Demo_Object.getClass()) {
      return false;
    }
    Employee other = (Employee) Demo_Object;
    return Objects.equals(Employee_Name, other.Employee_Name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(Employee_Name);
  }

  @Override
  public String toString() {
    return Employee_Name + " " + Employee_ID;
  }
}

public class Example {
  public static void main(String[] args) {
    List<Employee> Employee_List =
        List.of(new Employee("Sheeraz", 10), new Employee("John", 20), new Employee("Sheeraz", 30),
            new Employee("Robert", 40), new Employee("Jake", 50), new Employee("Logan", 60));
    // the distinct() will remove the duplicates by equals
    Employee_List.stream().distinct().forEach(System.out::println);
  }
}

El código de arriba usará el método distinct() en una lista de clases de Empleados usando la secuencia y eliminará las entradas con los mismos valores. El código muestra el uso simple del método distinct().

Ahora, veamos cómo aplicar una lógica distinta por propiedad en Java.

Use Collectors.toMap para aplicar Distinct by Property en Java

Podemos usar Collectors.toMap para recopilar los elementos de un flujo en un mapa para que la propiedad o el campo lo introduzcan. El mapa solo puede tener un valor para una clave, por lo que debemos elegir el primer objeto de flujo para cada clave.

Podemos llamar a los valores () del mapa resultante, que aplica la lógica distinta y nos da los valores únicos por una propiedad personalizada que agrupamos. Veamos un ejemplo:

package delftstack;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

class Employee {
  private String Employee_Name;
  private int Employee_ID;

  public Employee(String Employee_Name, int Employee_ID) {
    this.Employee_Name = Employee_Name;
    this.Employee_ID = Employee_ID;
  }

  public String getName() {
    return Employee_Name;
  }

  public int getID() {
    return Employee_ID;
  }

  @Override
  public boolean equals(Object Demo_Object) {
    if (this == Demo_Object) {
      return true;
    }
    if (Demo_Object == null) {
      return false;
    }
    if (getClass() != Demo_Object.getClass()) {
      return false;
    }
    Employee other = (Employee) Demo_Object;
    return Objects.equals(Employee_Name, other.Employee_Name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(Employee_Name);
  }

  @Override
  public String toString() {
    return Employee_Name + " " + Employee_ID;
  }
}

public class Example {
  public static void main(String[] args) {
    List<Employee> Employee_List =
        List.of(new Employee("Sheeraz", 10), new Employee("John", 20), new Employee("Sheeraz", 30),
            new Employee("Robert", 40), new Employee("Jake", 50), new Employee("Logan", 60));

    Collection<Employee> Unique_EmployeeList =
        Employee_List.stream()
            .collect(Collectors.toMap(
                Employee::getName, Function.identity(), (Employee1, Employee2) -> Employee1))
            .values();
    System.out.println(Unique_EmployeeList);
  }
}

El código anterior aplicará la lógica distinta en la secuencia utilizando el método Collection.toMap basado en la propiedad Employee_Name, lo que significa que el código devolverá una lista con nombres de empleados únicos. Ver salida:

[Jake 50, Logan 60, Robert 40, John 20, Sheeraz 10]

Como podemos ver, la lista de Empleados tiene dos entradas con el nombre Sheeraz, y una de ellas se elimina porque aplicamos una propiedad distinta basada en el nombre.

Use una clase contenedora para aplicar Distinct by Property en Java

También podemos crear una clase contenedora, asignar propiedades a la clase Empleado y luego aplicar el método distinct() por propiedad de la clase contenedora. La clase contenedora será estática privada y se declarará en la clase de controlador.

Ver ejemplo:

package delftstack;

import java.util.List;
import java.util.Objects;

class Employee {
  private String Employee_Name;
  private int Employee_ID;

  public Employee(String Employee_Name, int Employee_ID) {
    this.Employee_Name = Employee_Name;
    this.Employee_ID = Employee_ID;
  }

  public String getName() {
    return Employee_Name;
  }

  public int getID() {
    return Employee_ID;
  }

  @Override
  public boolean equals(Object Demo_Object) {
    if (this == Demo_Object) {
      return true;
    }
    if (Demo_Object == null) {
      return false;
    }
    if (getClass() != Demo_Object.getClass()) {
      return false;
    }
    Employee other = (Employee) Demo_Object;
    return Objects.equals(Employee_Name, other.Employee_Name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(Employee_Name);
  }

  @Override
  public String toString() {
    return Employee_Name + " " + Employee_ID;
  }
}

public class Example {
  private static class EmployeeWrapper {
    private Employee Employee;
    private EmployeeWrapper(Employee Employee) {
      this.Employee = Employee;
    }
    public Employee getEmployee() {
      return Employee;
    }
    @Override
    public boolean equals(Object Demo_Object) {
      if (this == Demo_Object) {
        return true;
      }
      if (Demo_Object == null) {
        return false;
      }
      if (getClass() != Demo_Object.getClass()) {
        return false;
      }
      EmployeeWrapper other = (EmployeeWrapper) Demo_Object;
      return Objects.equals(Employee.getName(), other.Employee.getName());
    }
    @Override
    public int hashCode() {
      return Objects.hash(Employee.getName());
    }
  }

  public static void main(String[] args) {
    List<Employee> Employee_List =
        List.of(new Employee("Sheeraz", 10), new Employee("John", 20), new Employee("Sheeraz", 30),
            new Employee("Robert", 40), new Employee("Jake", 50), new Employee("Logan", 60));

    Employee_List.stream()
        .map(EmployeeWrapper::new)
        .distinct()
        .map(EmployeeWrapper::getEmployee)
        .forEach(System.out::println);
  }
}

El código anterior usa la clase contenedora EmployeeWrapper con el método distinct() de la secuencia para obtener la lista de empleados con nombres únicos. Podemos ver que en la clase contenedora, hemos asignado el nombre de la propiedad en el método equals(), y luego lo usamos en la clase controladora.

Ver la salida:

Sheeraz 10
John 20
Robert 40
Jake 50
Logan 60

Use el método distinctByKey para aplicar Distinct by Property en Java

En este método, tenemos que usar el mapa hash concurrente para averiguar si existe alguna clave con el mismo valor. Este método se declarará en la clase de controlador y luego se usará en la transmisión con el método filter().

Ver el ejemplo:

package delftstack;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

class Employee {
  private String Employee_Name;
  private int Employee_ID;

  public Employee(String Employee_Name, int Employee_ID) {
    this.Employee_Name = Employee_Name;
    this.Employee_ID = Employee_ID;
  }

  public String getName() {
    return Employee_Name;
  }

  public int getID() {
    return Employee_ID;
  }

  @Override
  public boolean equals(Object Demo_Object) {
    if (this == Demo_Object) {
      return true;
    }
    if (Demo_Object == null) {
      return false;
    }
    if (getClass() != Demo_Object.getClass()) {
      return false;
    }
    Employee other = (Employee) Demo_Object;
    return Objects.equals(Employee_Name, other.Employee_Name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(Employee_Name);
  }

  @Override
  public String toString() {
    return Employee_Name + " " + Employee_ID;
  }
}

public class Example {
  public static <T> Predicate<T> distinctByKey(Function<? super T, Object> Key_Extractor) {
    Map<Object, Boolean> Employee_Map = new ConcurrentHashMap<>();
    return t -> Employee_Map.putIfAbsent(Key_Extractor.apply(t), Boolean.TRUE) == null;
  }

  public static void main(String[] args) {
    List<Employee> Employee_List =
        List.of(new Employee("Sheeraz", 10), new Employee("John", 20), new Employee("Sheeraz", 30),
            new Employee("Robert", 40), new Employee("Jake", 50), new Employee("Logan", 60));

    // Get distinct objects by one key
    List<Employee> Distinct_Employees1 =
        Employee_List.stream().filter(distinctByKey(p -> p.getName())).collect(Collectors.toList());
    System.out.println(Distinct_Employees1);

    // Get distinct objects by one key
    List<Employee> Distinct_Employees2 =
        Employee_List.stream()
            .filter(distinctByKey(p -> p.getName() + " " + p.getID()))
            .collect(Collectors.toList());
    System.out.println(Distinct_Employees2);
  }
}

El código anterior intenta obtener los distintos elementos en función del nombre y, nuevamente, el nombre y la identificación. Ver la salida:

[Sheeraz 10, John 20, Robert 40, Jake 50, Logan 60]
[Sheeraz 10, John 20, Sheeraz 30, Robert 40, Jake 50, Logan 60]

Como podemos ver, la primera lista se distingue solo por la propiedad de nombre, y la segunda se distingue por las propiedades de nombre e ID.

Sheeraz Gul avatar Sheeraz Gul avatar

Sheeraz is a Doctorate fellow in Computer Science at Northwestern Polytechnical University, Xian, China. He has 7 years of Software Development experience in AI, Web, Database, and Desktop technologies. He writes tutorials in Java, PHP, Python, GoLang, R, etc., to help beginners learn the field of Computer Science.

LinkedIn Facebook

Artículo relacionado - Java Stream