5

I have already gone through few examples and those did not work for me.

Here is what I am trying to do:

I have a List<SomeClass> of the following class:

class SomeClass {
  String rid;
  String name;
  ...
}

The values in my List look like this:

SomeClass(1,"apple")
SomeClass(1,"banana")
SomeClass(1,"orange")
SomeClass(2,"papaya")
SomeClass(2,"peaches")
SomeClass(3,"melons")

I want to convert the above List into a Map<String, Set<String>>, where key is rid and value is Set of name field.

To solve this using Java Streams I am using groupingBy and I could come to below solution:

someClassList
            .stream()
            .map(SomeClass::getName)
            .collect(
                  Collectors.groupingBy(
                      SomeClass::getRid, Collectors.toSet()));

But this gives me compilation error. How do I solve this and what is the problem with my approach?

0

2 Answers 2

8

When you call .map(SomeClass::getName) on your Stream<SomeClass>, you get a Stream<String>. You can't execute collect(Collectors.groupingBy(SomeClass::getRid,...)) on a Stream<String> (you can only execute it on a Stream<SomeClass>). Therefore your map step is wrong.

You need to pass the Collector returned by Collectors.mapping() to Collectors.groupingBy() in order to map the SomeClass instances to Strings after they are grouped by getRid.

Map<String, Set<String>> map =
    someClassList.stream()
                 .collect(Collectors.groupingBy(SomeClass::getRid, 
                                                Collectors.mapping(SomeClass::getName,
                                                                   Collectors.toSet())));
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that was quick :)
3

Although not as readable as the groupingBy collector; you can use the toMap collector just as well:

myList.stream()
      .collect(toMap(SomeClass::getRid, e -> new HashSet<>(singleton(e.getName())), 
                 (l, r) -> {l.addAll(r); return l;}));

Ensure that you have the necessary imports for singleton and toMap or you can just use Collectors.toMap(...) and Collections.singleton(...) respectively.

1 Comment

That’s what I use when I expect most of the groups to be rather small. .collect(toMap(SomeClass::getRid, e -> singleton(e.getName()), (l, r) -> { if(l.size() == 1) { l = new HashSet<>(l); } l.addAll(r); return l;})); if you want to use it in parallel, it might be worth to extend the merge function to check the sizes and add the smaller set to the bigger set.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.