The Java 8 Stream supports various functionalities to handle aggregate operations in a sequence of elements.
You can use Java 8 Stream’s distinct function.stream().distinct()
to aggregate distinct values in a stream of elements.
In this tutorial, you will learn how to identify the distinct elements by property using Java 8.
Using Custom Object property
To get distinct value by property in a Java object,
- Iterate through the stream of objects and filter the stream(
stream.filter(predicateFn)
) based on a property by passing a predicate function. - Predicate function should pass the property as input(
car->car.getDoors()
) and will use a ConcurrentHashMap to hold the distinct value. - Any element in the stream is first checked in the map(
map.putIfAbsent()
); if the element is not found, that value will be added to the map and returned. If a specific element is already present in the map, it will not be added to the map and not returned; by this, distinct values are retained. - Convert the stream to a List using
.collect()
.
Use this method when distinct values are expected based on specific field/property in an object.
Code
import com.techmam.model.Car;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
//main
public static void main(String[] args) {
List<Car> carsList = Arrays.asList(new Car("Camaro ZL1",2),
new Car("Mustang",2),
new Car("AMG C63",2),
new Car("Mini Hatch", 3),
new Car("M3 Saloon", 4),
new Car("S4 Saloon", 4),
new Car("Taycan",4));
List<Car> distinctCars = carsList.stream().filter(distinctObjectsByProperty(car->car.getDoors())).collect(Collectors.toList());
distinctCars.forEach(e->{
System.out.println("Model: "+e.getName()+", No of Doors: "+e.getDoors());
});
}
//private method to filter the distinct value
private static <T> Predicate<T> distinctObjectsByProperty(Function<? super T, ?> key) {
Map<Object, Boolean> distinctValues = new ConcurrentHashMap<>();
return t -> distinctValues.putIfAbsent(key.apply(t), Boolean.TRUE) == null;
}
//Car.java
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class Car {
String name;
int doors;
}
Output
Model: Camaro ZL1, No of Doors: 2
Model: Mini Hatch, No of Doors: 3
Model: M3 Saloon, No of Doors: 4
Using Collectors.toMap()
To get a distinct value from a property using Collectors.toMap()
,
- Convert the list to stream using
.stream()
. - Use the
.collect(Collectors.toMap())
method to convert the stream of elements to a Map. - The
.toMap()
accepts a property as key (Car::getDoors
), the current object(Function.identity()
) as value and a merger function((car1, car2) -> car1
). - The merger function will internally use a HashMap to store each element when already not present, and that map is returned.
- Use the
map.values()
method to return only the distinct objects.
Use this method when the distinct values are required as a map with key-value pair based on the property.
Code
import com.techmam.model.Car;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
//main
List<Car> carsList = Arrays.asList(new Car("Camaro ZL1",2),
new Car("Mustang",2),
new Car("AMG C63",2),
new Car("Mini Hatch", 3),
new Car("M3 Saloon", 4),
new Car("S4 Saloon", 4),
new Car("Taycan",4));
Collection<Car> distinctCars = carsList.stream()
.collect(Collectors.toMap(Car::getDoors, Function.identity(), (car1, car2) -> car1)).values();
System.out.println(distinctCars);
Output
[Car(name=Camaro ZL1, doors=2), Car(name=Mini Hatch, doors=3), Car(name=M3 Saloon, doors=4)]
Distinct in Duplicate Objects
To Identify distinct values in a list of duplicate objects,
- Iterate the stream and invoke the distinct method(
.distinct
) method. - All the exact match duplicate values will be removed, and distinct stream values will be returned.
- Convert the stream as list using
.collect(Collectors.toList())
to get the distinct objects.
Use this method to identify the duplicate objects in a List.
Code
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
List<Car> carsListWithDuplicates = Arrays.asList(new Car("Mustang",2),
new Car("Mustang",2),
new Car("Mustang",2),
new Car("Mini Hatch", 3),
new Car("M3 Saloon",4),
new Car("M3 Saloon",4),
new Car("M3 Saloon",4));
// Get distinct people by id
List<Car> distinctCars = carsListWithDuplicates.stream()
.distinct().collect(Collectors.toList());
System.out.println(distinctCars);
Output
[Car(name=Mustang, doors=2), Car(name=Mini Hatch, doors=3), Car(name=M3 Saloon, doors=4)]
Distinct in Duplicate Values
To get a list of distinct values in a stream of objects,
- Iterate the stream and invoke
.map(property)
with the property. - Use the
.distinct()
method to return only the distinct property values. - Convert the distinct stream of values to List using
..collect(Collectors.toList().
Use this method to find distinct values of an object property.
Code
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
//main
List<Car> carsList = Arrays.asList(new Car("Camaro ZL1",2),
new Car("Mustang",2),
new Car("AMG C63",2),
new Car("Mini Hatch", 3),
new Car("M3 Saloon", 4),
new Car("S4 Saloon", 4),
new Car("Taycan",4));
List<Integer> distinctCarDoorValues= carsList.stream().map(Car::getDoors).distinct().collect(Collectors.toList());
distinctCarDoorValues.forEach(System.out::println);
Output
2
3
4
The below examples explain getting distinct values in an Integer stream
Use this method for any primitive datatype streams.
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
//main
Stream<Integer> numbersStream = Stream.of(3,4,5,6,3,4,6,7,4,5,6,7);
List<Integer> distinctNumbers = numbersStream.distinct().collect(Collectors.toList());
System.out.println(distinctNumbers);
Output
[3, 4, 5, 6, 7]