Obtain the Elements of a Collection having the identical subset of Properties using Stream API

Viewed 70

I have the following code:

List<MyObject> filteredObjects = myObjects.stream()
    .filter(anObject -> 
        Collections.frequency(myObjects, anObject) > 1
    )
    .collect(Collectors.toList());

Which does the job of getting me the objects from the Collection of MyObject that occur more than once.

I need to modify this code so that it gives me the objects that match one another not according to the equals(), but based on a subset of properties.

So if MyObject has properties: foo, bar and baz.

public static class MyObject {
    private Foo foo;
    private Bar bar;
    private Baz baz;
    
    // constructor, getters, etc.
}

I want the to get the objects that have a corresponding object in the collection with the same values of foo and bar.

1 Answers

You can create a custom object that encompasses these properties (foo, bar) that require to match and generate an auxiliary Map using such objects as a key.

In the code below for conciseness I'm using Java 16 record to define a key, if you're using an earlier JDK version it can be replaced with a class.

record Key(Object foo, Object bar) {
    public Key(MyObject o) {
        this(o.getFoo(), o.getBar());
    }
}

When you have a Map associating all objects having equal set of properties with the same key on your hands, the only thing left is to filter the values having size greater than 1.

List<MyObject> filteredObjects = myObjects.stream()
    .collect(Collectors.groupingBy( // creates an intermediate Map<Key, List<MyObject>>
        Key::new
    ))
    .values().stream()               // Stream<List<MyObject>>
    .filter(list -> list.size() > 1)
    .flatMap(List::stream)           // Stream<MyObject>
    .toList();                       // for Java 16+ or collect(Collectors.toList()) for earlier versions

In case if you need to preserve the encounter order of objects, you can use another version of the collector groupingBy() which allows to specify mapFactory:

Collectors.groupingBy(
    Key::new,
    LinkedHashMap::new,
    Collectors.toList()
)
Related