How to Distinct by Property in Java 8 Stream

Sheeraz Gul Feb 15, 2024
  1. Importance of Distinct by Property
  2. Methods to Achieve Distinct by Property in Java 8 Streams
  3. Using the Collectors.toMap() Method
  4. Using the Collectors.groupingBy() Method
  5. Using the Wrapper Class
  6. Using the map(), distinct(), and collect() Methods
  7. Using the distinctByKey Method
  8. Using the HashMap Method
  9. Conclusion
How to Distinct by Property in Java 8 Stream

Explore the significance of achieving distinct by property in Java 8 streams and various methods for streamlined and efficient implementation.

Importance of Distinct by Property

Distinct by property using streams in Java is crucial for efficiently handling data with duplicate values. This functionality allows developers to filter and retrieve unique elements based on specific attributes, providing clarity and precision in data manipulation.

Whether working with collections of objects or key-value pairs, the ability to obtain distinct instances based on a chosen property enhances code readability and simplifies complex operations. By eliminating redundancy, this feature not only optimizes code but also ensures more accurate and meaningful results.

Distinct by property in Java streams is essential for clean and effective data processing, enabling developers to focus on unique characteristics and streamline their code.

Methods to Achieve Distinct by Property in Java 8 Streams

Using the Collectors.toMap() Method

Using Collectors.toMap in Java 8 streams is crucial for achieving distinct by property efficiently. It allows developers to create a Map where the property acts as the key, ensuring uniqueness.

The method provides a clear and concise way to handle potential duplicates, offering control over which values to keep. Collectors.toMap simplifies the code, enhances readability, and offers a powerful solution for distinct by property in Java 8 streams.

Consider a situation where we have a list of Person objects, and we want to obtain distinct persons based on their names. Using the Collectors.toMap() method allows us to achieve this distinct by property functionality in a streamlined manner.

Code Example:

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

class Person {
  private String name;

  public Person(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

public class DistinctByPropertyExample {
  public static void main(String[] args) {
    List<Person> persons = List.of(new Person("Alice"), new Person("Bob"), new Person("Alice"),
        new Person("Charlie"), new Person("Bob"));

    Map<String, Person> distinctByName = persons.stream().collect(Collectors.toMap(
        Person::getName, Function.identity(), (existing, replacement) -> existing));

    distinctByName.values().forEach(person -> System.out.println(person.getName()));
  }
}

In this example, we utilize the Person class to represent objects with a name property. The DistinctByPropertyExample class demonstrates the creation of a list of Person objects, potentially containing duplicates.

To achieve distinct elements based on the name property, we employ the Collectors.toMap() method. This method transforms the list into a Map, using the name property as the key and the entire Person object as the value.

The third argument in toMap() ensures that if there are duplicates, the existing value is retained. Finally, we extract the distinct persons from the map and print their names.

The output of this code will be:

distinct by prop - tomap

This showcases the effectiveness of using Collectors.toMap() to achieve distinct by property in Java 8 streams. The resulting map contains only unique persons based on their names, providing a concise and efficient solution to the distinct by property problem.

Using the Collectors.groupingBy() Method

Utilizing Collectors.groupingBy() in Java 8 streams is essential for achieving distinct by property. It efficiently organizes elements into groups based on the specified property, providing a clear structure.

By grouping elements, it facilitates easy extraction of distinct values, ensuring a concise and readable code. Unlike other methods, Collectors.groupingBy() emphasizes the property as a grouping criterion, making it particularly effective for scenarios where distinct elements are based on specific attributes.

Consider a scenario where we have a list of Person objects, and we want to obtain distinct persons based on their names. Using Collectors.groupingBy() allows us to group elements by their name property and select the first element from each group, effectively achieving distinct by property.

Code Example:

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

class Person {
  private String name;

  public Person(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

public class DistinctByPropertyExample {
  public static void main(String[] args) {
    List<Person> persons = List.of(new Person("Alice"), new Person("Bob"), new Person("Alice"),
        new Person("Charlie"), new Person("Bob"));

    List<Person> distinctByName = persons.stream()
                                      .collect(Collectors.groupingBy(Person::getName))
                                      .values()
                                      .stream()
                                      .map(list -> list.get(0))
                                      .collect(Collectors.toList());

    distinctByName.forEach(person -> System.out.println(person.getName()));
  }
}

In this example, we introduce a Person class to represent objects with a name property. The DistinctByPropertyExample class demonstrates the creation of a list of Person objects, which may contain duplicates.

To achieve distinct elements based on the name property, we employ the Collectors.groupingBy() method. This method organizes the persons into groups based on their names, resulting in a Map where each key is a unique name, and the corresponding value is a list of persons with that name.

By extracting the first person from each group, the code efficiently obtains distinct persons based on their names.

distinct by prop - groupby

This illustrates the effectiveness of using Collectors.groupingBy() to achieve distinct by property in Java 8 streams. The resulting list contains only unique persons based on their names, providing a concise and efficient solution to the distinct by property problem.

Using the Wrapper Class

Utilizing a Wrapper class in Java 8 streams for achieving distinct by property is crucial for providing a structured and flexible solution. This approach allows developers to encapsulate the key extraction logic within the Wrapper, enhancing code organization and reusability.

The Wrapper’s implementation of equals() and hashCode() ensures proper distinction based on the chosen property, offering clarity and consistency. Compared to other methods, like using custom filtering or collectors, the Wrapper class approach emphasizes encapsulation and abstraction, contributing to cleaner code and improved maintainability.

This technique proves particularly valuable when dealing with more complex scenarios, providing a concise and elegant solution for obtaining distinct elements based on specific attributes in Java 8 streams.

Consider a scenario where we have a list of Person objects, and we want to obtain distinct persons based on their names. Using a Wrapper class and the distinct() method, we can achieve this in an elegant manner.

Code Example:

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

class Person {
  private String name;

  public Person(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

class Wrapper<T, U> {
  private final T keyExtractor;
  private final U value;

  public Wrapper(T keyExtractor, U value) {
    this.keyExtractor = keyExtractor;
    this.value = value;
  }

  public T getKeyExtractor() {
    return keyExtractor;
  }

  public U getValue() {
    return value;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o)
      return true;
    if (o == null || getClass() != o.getClass())
      return false;
    Wrapper<?, ?> wrapper = (Wrapper<?, ?>) o;
    return Objects.equals(keyExtractor, wrapper.keyExtractor);
  }

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

public class DistinctByPropertyExample {
  public static void main(String[] args) {
    List<Person> persons = List.of(new Person("Alice"), new Person("Bob"), new Person("Alice"),
        new Person("Charlie"), new Person("Bob"));

    List<Person> distinctByName = persons.stream()
                                      .map(person -> new Wrapper<>(person.getName(), person))
                                      .distinct()
                                      .map(Wrapper::getValue)
                                      .collect(Collectors.toList());

    distinctByName.forEach(person -> System.out.println(person.getName()));
  }
}

In this example, we introduce a Person class with a name property and a Wrapper class for achieving distinct by property. The DistinctByPropertyExample class demonstrates the creation of a list of Person objects, potentially containing duplicates.

To achieve distinct elements based on the name property, we use the Wrapper class. Each Person object is mapped to a Wrapper containing its name and the original object.

The distinct() method then eliminates duplicates based on the name. Mapping back to the original Person object and collecting the distinct persons into a list offers a clear and efficient solution for achieving distinct by property in Java 8 streams.

The output of this code will be:

distinct by prop - wrapper

This demonstrates the effectiveness of using a Wrapper class and the distinct() method to achieve distinct by property in Java 8 streams. The resulting list contains only unique persons based on their names, providing a clear and efficient solution to the distinct by property problem.

Using the map(), distinct(), and collect() Methods

Utilizing map(), distinct(), and collect() in Java 8 streams is vital for achieving distinct by property. This combination allows developers to efficiently transform and filter elements based on a specific attribute, ensuring uniqueness.

The use of map() facilitates the extraction of property values, distinct() ensures only unique values are retained, and collect() brings them together into a streamlined result.

This concise approach enhances code readability, simplifies the distinct by property process, and provides an effective solution for Java 8 streams.

Let’s consider a scenario where we have a list of Person objects, and we want to obtain distinct persons based on their names. Using map(), distinct(), and collect(), we can achieve this in a straightforward manner.

Code Example:

import java.util.List;
import java.util.stream.Collectors;

class Person {
  private String name;

  public Person(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

public class DistinctByPropertyExample {
  public static void main(String[] args) {
    List<Person> persons = List.of(new Person("Alice"), new Person("Bob"), new Person("Alice"),
        new Person("Charlie"), new Person("Bob"));

    List<Person> distinctByName = persons.stream()
                                      .map(Person::getName)
                                      .distinct()
                                      .map(name
                                          -> persons.stream()
                                                 .filter(person -> person.getName().equals(name))
                                                 .findFirst()
                                                 .orElse(null))
                                      .collect(Collectors.toList());

    distinctByName.forEach(person -> System.out.println(person.getName()));
  }
}

In this example, we introduce a Person class with a name property. The DistinctByPropertyExample class showcases the creation of a list of Person objects, potentially containing duplicates.

In order to achieve distinct elements based on the name property, we leverage the combination of map(), distinct(), and collect() in Java 8 streams. The map() operation extracts names, distinct() ensures uniqueness, and another map() retrieves the first Person with each unique name.

The output of this code will be:

distinct by prop - map distinct collect

This demonstrates the effectiveness of using map(), distinct(), and collect() to achieve distinct by property in Java 8 streams. The resulting list contains only unique persons based on their names, providing a concise and expressive solution to the distinct by property problem.

Using the distinctByKey Method

Leveraging distinctByKey in Java 8 streams is pivotal for achieving distinct by property. This custom method allows developers to streamline the process by focusing on a specific property, providing a clearer and more reusable solution.

It ensures uniqueness based on a chosen property, offering a flexible and efficient way to handle duplicates. This method empowers developers to express their intent more explicitly, making it a valuable tool for obtaining distinct elements by property in Java 8 streams.

Consider a scenario where we have a list of Person objects, and we want to obtain distinct persons based on their names. Using a custom distinctByKey method, we can achieve this in a streamlined and readable manner.

Code Example:

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

class Person {
  private String name;

  public Person(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

public class DistinctByPropertyExample {
  public static void main(String[] args) {
    List<Person> persons = List.of(new Person("Alice"), new Person("Bob"), new Person("Alice"),
        new Person("Charlie"), new Person("Bob"));

    List<Person> distinctByName =
        persons.stream().filter(distinctByKey(Person::getName)).collect(Collectors.toList());

    distinctByName.forEach(person -> System.out.println(person.getName()));
  }

  private static <T> java.util.function.Predicate<T> distinctByKey(
      Function<? super T, ?> keyExtractor) {
    java.util.Map<Object, Boolean> seen = new java.util.HashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
  }
}

In this example, we introduce a Person class representing objects with a name property. The DistinctByPropertyExample class demonstrates the creation of a list of Person objects, potentially containing duplicates.

To achieve distinct elements based on the name property, we create a custom distinctByKey method. This method, taking a key extractor function, filters the stream based on the uniqueness of the extracted keys.

We then apply distinctByKey to the stream of Person objects, using the getName method as the key extractor. This ensures that the resulting list contains only distinct persons based on their names.

The output of this code will be:

distinct by prop - distinctbykey

This demonstrates the effectiveness of creating a custom distinctByKey method to achieve distinct by property in Java 8 streams. The resulting list contains only unique persons based on their names, providing a clear and reusable solution to the distinct by property problem.

Using the HashMap Method

Utilizing HashMap in Java 8 streams is crucial for achieving distinct by property. It offers a concise and efficient solution by leveraging the unique key property of the map.

This method contrasts with alternatives like custom filtering or collectors, providing a straightforward approach for obtaining distinct elements based on a specific attribute. The simplicity and effectiveness of using HashMap make it a valuable tool for stream processing in Java 8, enhancing code clarity and performance.

Let’s consider a scenario where we have a list of Person objects, and we want to obtain distinct persons based on their names. We’ll leverage the power of streams and a HashSet to achieve this distinct by property functionality.

Code Example:

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

class Person {
  private String name;

  public Person(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

public class DistinctByPropertyExample {
  public static void main(String[] args) {
    List<Person> persons = List.of(new Person("Alice"), new Person("Bob"), new Person("Alice"),
        new Person("Charlie"), new Person("Bob"));

    Set<String> seen = new HashSet<>();
    List<Person> distinctByName =
        persons.stream().filter(p -> seen.add(p.getName())).collect(Collectors.toList());

    distinctByName.forEach(person -> System.out.println(person.getName()));
  }
}

In this example, we introduce a Person class representing objects with a name property. The DistinctByPropertyExample class showcases the creation of a list of Person objects, potentially containing duplicates.

To achieve distinct elements based on the name property, we employ a HashSet named seen. Iterating through the stream of persons, the filter operation ensures only the first occurrence of each name is added to the result list, effectively achieving distinct by property.

The output of this code will be:

distinct by prop - hash

This demonstrates the effectiveness of using a HashSet to achieve distinct by property in Java 8 streams. The resulting list contains only unique persons based on their names, providing a concise and efficient solution to the distinct by property problem.

Conclusion

Achieving distinct by property in Java 8 streams is essential for effective data processing, allowing developers to filter unique elements based on specific attributes. The methods discussed in this article each offer distinct advantages.

These methods enhance code readability, maintainability, and performance, providing flexible and concise solutions for different scenarios. Whether leveraging built-in collectors or crafting custom solutions, Java 8 streams empower developers to streamline distinct by property operations, resulting in cleaner code and improved efficiency in handling collections.

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