RichK RichK - 3 months ago 287
Java Question

Java 8 Distinct by property

In Java 8 how can I filter a collection using the

Stream
API by checking the distinctness of a property of each object?

For example I have a list of
Person
object and I want to remove people with the same name,

persons.stream().distinct();


Will use the default equality check for a
Person
object, so I need something like,

persons.stream().distinct(p -> p.getName());


Unfortunately the
distinct()
method has no such overload. Without modifying the equality check inside the
Person
class is it possible to do this succinctly?

Answer

I finally figured out a nice way to do this. Consider distinct to be a stateful filter. Write function that returns a predicate that also maintains state about what it's seen previously, and returns whether the given element was seen for the first time:

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

Then you can write:

persons.stream().filter(distinctByKey(p -> p.getName());

Note: this is essentially the same as my answer to this question:Java Lambda Stream Distinct() on arbitrary key?