Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

149
Views
How to neglect objects during stream grouping or partitioning?

It is possible to neglect some elements during Collectors.groupingBy or Collectors.partitioningBy?

Of course I know I can place a .filter() anywhere in a stream. But my problem is that I have to run a rather complex and expensive evaluation, to decide in which "group" my objects should be divided into.

Moreover, there are always many objects that I would want to neglect during collecting.

Example: imagine a List<Foo>, that I want to split into 2 lists. That's easy, but how can I additionally neglect all objects that don't fit into my evaluated condition?

var map = foos.stream().collect(Collectors.groupingBy(
    foo -> {
        int bar = complexEvaluation(foo);
        if (bar > 1000) return true;
        if (bar < 0) return false;
        //TODO how to neglect the rest between 0-1000
    },
    Collectors.mapping(foo -> foo.id, Collectors.toSet())
));
over 4 years ago · Santiago Trujillo
4 answers
Answer question

0

To reuse the result of complexEvaluation in both filter and groupingBy, you can call before filtering and store the result in a wrapper class.

foos.stream()
    .map(foo -> {
        int bar = complexEvaluation(foo);
        if (bar > 1000) Pair.of(foo, true);
        if (bar < 0) Pair.of(foo, false);
        return Pair.of(foo, null);
    )
    .filter(fooPair -> fooPair.getRight() != null)
    .collect(Collectors.groupingBy(
        Pair::getRight(),
        Collectors.mapping(fooPair -> fooPair.getLeft().id, Collectors.toSet()
    );

However, this is only if you insist on using groupingBy for some reason.

An alternative with foreach would be much easier to read:

Map<Boolean, Set<Foo>> groups = new HashMap<>();
foos.stream()
    .forEach(foo -> {
        int bar = complexEvaluation(foo);
        if (bar > 1000) groups.computeIfAbsent(true, k->new HashSet<>()).add(foo);
        if (bar < 0)    groups.computeIfAbsent(false, k->new HashSet<>()).add(foo);
   })
    
over 4 years ago · Santiago Trujillo Report

0

If I understand your intent correctly you want to filter out some values while collecting them. It's doable with Collectors.filtering().

Note, that filtering() can eliminate all values for some buckets, but it'll not cause empty buckets to be removed. In the example below buckets for 1, 2, 3, 4, 5 will be empty.

    public static void main(String[] args) {
        var foos = List.of(-100, 1, 2, 3, 4, 5, 1001, 1002, 1003);
        
        var map = foos.stream()
                .collect(Collectors.groupingBy(
                            UnaryOperator.identity(),
                            Collectors.filtering(foo -> foo < 0 || foo > 1000,
                                    Collectors.toSet())));

        System.out.println(map);
    }

map

{1=[], 2=[], 3=[], -100=[-100], 4=[], 5=[], 1001=[1001], 1002=[1002], 1003=[1003]}

UPDATE

I revised this problem and the solution provided below computes the values beforehand.

In this version, undesired entries are filtered out in the stream, which makes the code inside Collector easier to read. I hope that instead of foo objects integer is used isn't a problem.

    public static void main(String[] args) {
        List<Integer> foos = List.of(-100, 1, 2, 3, 4, 5, 101, 102, 103);

        Map<Integer, Integer> fooToValue = getFooToValueMap(foos);

        Map<Boolean, Set<Integer>> map = getFoosMap(fooToValue);

        System.out.println(map);
    }
    private static Map<Boolean, Set<Integer>> getFoosMap(Map<Integer, Integer> fooToValue) {
        return fooToValue.entrySet().stream()
                .filter(entry -> entry.getValue() < 0 || entry.getValue() > 1000)
                .collect(Collectors.partitioningBy(
                            entry -> entry.getValue() > 1000,
                            Collectors.mapping(Map.Entry::getKey, Collectors.toSet())
                ));
    }
    private static Map<Integer, Integer> getFooToValueMap(List<Integer> list) {
        return list.stream()
                .collect(Collectors.toMap(UnaryOperator.identity(), foo -> complexEvaluation(foo)));
    }
    private static int complexEvaluation(int foo) {
        return (int) Math.signum(foo) * foo * foo;
    }

map

{false=[-100], true=[101, 102, 103]}
over 4 years ago · Santiago Trujillo Report

0

Just use an enum to define your 3 cases:

enum Categories {
    HIGH, LOW, NEGATIVE
}

var map = foos.stream().collect(Collectors.groupingBy(
    foo -> {
        int bar = complexEvaluation(foo);
        if (bar > 1000) return HIGH;
        if (bar < 0) return NEGATIVE;
        return LOW;
    },
    Collectors.mapping(foo -> foo.id, Collectors.toSet())
));

Then ignore or remove LOW if you don’t need it. It also has the added benefits of giving more meaning to your categories instead of just naming them true/false, and making it easier to refactor if you need more categories in the future.

Only drawback is that it builds a useless LOW set, but that’s only an issue if it is really big compared to the other sets and the complexEvaluation() operation.

over 4 years ago · Santiago Trujillo Report

0

If you want to avoid temporary storage, you have to implement your own collector:

var map = foos.parallelStream().collect(
    () -> Map.of(true, new HashSet<ID>(), false, new HashSet<ID>()),
    (o, foo) -> {
        int bar = complexEvaluation(foo);
        ID id = foo.id;
        if (bar > 1000) o.get(true).add(id);
        else if (bar < 0) o.get(false).add(id);
    },
    (a, b) -> { a.get(true).addAll(b.get(true)); a.get(false).addAll(b.get(false)); }
);

This example has the same behavior as partitioningBy, always creating entries for true and false.

ID is a placeholder for the type of foo.id which you didn’t include in the question.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!