Search code examples
javafilteringjava-streamdistinct-values

Java stream that is distinct by more than one property


I have following objects in the stream:

class Foo{
    String a;
    String b;
    int c;
}

I would like to filter a stream based on following criteria:

eg. Having entries in stream: foo1 and foo2:

foo1 and foo2 have same values for a and b, but they differ in c property.

I would like to get rid of entries that have c higher in such case.


Solution

  • Semantically equivalent to Eugene’s answer, but a bit simpler:

    List<Foo> foos = Stream.of(new Foo("a", "b", 1), new Foo("a", "b", 2),
                     new Foo("a", "b", 3), new Foo("a", "bb", 3), new Foo("aa", "b", 3))
        .collect(Collectors.collectingAndThen(
            Collectors.toMap(x -> Arrays.asList(x.getA(), x.getB()), x -> x,
                             BinaryOperator.minBy(Comparator.comparing(Foo::getC))),
                map -> new ArrayList<>(map.values())));
    

    You need to group by a key holding both properties and due to the absence of a standard Pair type, you may use a List with two elements or a Map.Entry, both work. But using List is simpler (in Java 9, you would use List.of(…, …) which is even simpler) and has a better hash code if the same values may occur in both properties.

    When the dowstream operation is a pure reduction, like selecting the minimum of the C property, the toMap collector fits better as it doesn’t require dealing with Optional.