Distinct by Property in Java 8 Stream

Sheeraz Gul Jan 30, 2023 Sep 22, 2022
  1. Distinct by Property in Java 8 Stream
  2. Use Collectors.toMap to Apply the Distinct by Property in Java
  3. Use a Wrapper Class to Apply the Distinct by Property in Java
  4. Use the distinctByKey Method to Apply the Distinct by Property in Java
Distinct by Property in Java 8 Stream

This tutorial demonstrates the distinct functionality by-property using the stream in Java.

Distinct by Property in Java 8 Stream

The Java 8 Stream has a method distinct() that filters the duplicates out of a list. This method uses the equal method from the instance to check the unique elements and if it sees duplicates, then remove them.

We cannot use the distinct() method if it is required to be applied on a certain property or field. But we can apply the distinct() method by property using a particular way.

Let’s try an example where we create a class Employee which has the name and id of the employee and then try to use the distinct() method in the stream on the Employee class list:

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);
    }
}

The code above will use the distinct() method on a list of Employee classes using the stream and will delete the entries with the same values. The code shows the simple use of the distinct() method.

Now, let’s see how to apply distinct logic by property in Java.

Use Collectors.toMap to Apply the Distinct by Property in Java

We can use the Collectors.toMap to collect the elements of a stream into a map to be keyed by the property or field. The map can only have one value for one key, so we need to choose the first stream object for each key.

We can call the values() from the resulting map, which applies the distinct logic and gives us the unique values by a custom property we grouped. Let’s see an example:

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);
    }
}

The code above will apply the distinct logic on the stream using the Collection.toMap method based on the Employee_Name property, which means the code will return a list with unique employee names. See output:

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

As we can see, the Employee list has two entries with the name Sheeraz, and one of them is deleted because we applied distinct based on the name property.

Use a Wrapper Class to Apply the Distinct by Property in Java

We can also create a wrapper class, assign properties to the Employee class, and then apply the distinct() method by property from the wrapper class. The wrapper class will be private static and declared in the driver class.

See example:

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);

    }
}

The code above uses the wrapper class EmployeeWrapper with the distinct() method from the stream to get the list of employees with unique names. We can see that in the wrapper class, we have assigned the property name in the equals() method, and then we use it in the driver class.

See the output:

Sheeraz 10
John 20
Robert 40
Jake 50
Logan 60

Use the distinctByKey Method to Apply the Distinct by Property in Java

In this method, we have to use the concurrent hash map to find out if there is any existing key with the same value. This method will be declared in the driver class and then used in the stream with the filter() method.

See the example:

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 );
    }
}

The code above tries to get the distinct elements based on the name and, again, the name and ID. See the output:

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

As we can see, the first list is distinct by only the name property, and the second is distinct by both name and ID properties.

Author: Sheeraz Gul
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

Related Article - Java Stream