Finkelson
Finkelson

Reputation: 3013

Java: How to group entities by condition using Stream API?

I have a list of users.

class User {
    String name;
}

And I need to group users by name.

List<Set<User>> groupedBy;

The grouping condition is the name similarity, it's some Comparator

Comparator<User> p = new Comparator<User>() {
    // some comparison logic
};

How can I group users by similar name using Java 8 streams?

I've tried to use partitioning, but it has a different API than I need.

Map<Boolean, List<User>> collected = users.stream().collect(Collectors.partitioningBy(new Predicate<User>() {
    @Override
    public boolean test(User user) {
        // ...
    }
}, Collectors.toList()));

Upvotes: 1

Views: 6318

Answers (1)

Holger
Holger

Reputation: 298123

Normally, if you want to group via a simple property, you’d use

Map<String,Set<User>> m=users.stream()
    .collect(Collectors.groupingBy(User::getName, Collectors.toSet()));

however, if you have a complicated logic that is expressed as an already existing Comparator, e.g.

Comparator<User> comparator=Comparator.comparing(u -> u.getName(), Collator.getInstance());

you can use

Map<User,Set<User>> m=users.stream().collect(
    Collectors.groupingBy(u -> u, () -> new TreeMap<>(comparator), Collectors.toSet()));

to group according to the logic encapsulated in the comparator.

In both cases, you get a Map which can be converted to your desired list using

List<Set<User>> groupedBy = new ArrayList<>(m.values());

You can also express the last step as part of the Collector, e.g.

List<Set<User>> groupedBy=users.stream().collect(
    Collectors.collectingAndThen(
        Collectors.groupingBy(u -> u, () -> new TreeMap<>(comparator), Collectors.toSet()),
        m -> new ArrayList<Set<User>>(m.values())));

Upvotes: 4

Related Questions