Java 8 Stream flatMap and group by code compiler error
Asked Answered
R

2

3
// given a set of Item objects, group them by the managers of creator and owners
Map<String, List<Item>> managersItems = 
    itemSet.parallelStream().flatMap(item -> {
        // get the list of the creator and owners
        List<String> users = new ArrayList();
        users.add(item.getCreator());
        users.addAll(item.getOwners());
        return Stream.of(users.toArray(new String[] {})).map(user -> {
            LdapUserInfo ldapUser = LdapUserInfoFactory.create(user);
            String manager = ldapUser.getManager();
            return new AbstractMap.SimpleImmutableEntry<String, Item(manager, item);
        });
    }).collect(
        Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

This code compiles fine in Eclipse Mars, but gets the following eror in Eclipse Luna:

Type mismatch: cannot convert from Map<Object,List<Object>> to Map<String,List<WeblabInfo>>

If I do not assign the returned to a Map with Map<String, List<Item>> managersItem = in Eclipse Luna, the error is at Map.Entry::getKey and Map.Entry::getValue statement with message:

The type Map.Entry does not define getKey(Object) that is applicable here".

What did I do wrong?

Rowan answered 12/10, 2015 at 23:47 Comment(0)
A
4

You didn't do anything wrong. Eclipse compiler has problems with type inference that causes these issues. If Luna compatibility is important, you will have to add explicit types to lambda expressions. Try, for example, Map.Entry::<String,Item>getKey

On another note, it's not necessary to convert a List to array to stream it. You can directly call users.stream(). But even creating the List isn't necessary. You can use Stream.concat(Stream.of(item.getCreator()), item.getOwners().stream()) instead (granted, it's a bit unwieldy).

Finally (and most importantly), avoid using parallelStream with blocking code, such as looking up data in an external system. Parallel streams are designed to handle CPU-bound tasks.

Altostratus answered 13/10, 2015 at 1:23 Comment(2)
Thanks for the great tips! The final solution also includes casting the returned type of flatMap(...) to <Map.Entry<String, Item>>. The working code is posted below.Rowan
Note for the interested reader: Eclipse Mars not only contains many bug fixes but also adopted several improvements in JLS that have been made post the release of Java 8. Some of these changes particularly improve type inference in nested lambdas / invocations. I haven't checked though, whether the original JLS 8 would allow inference to succeed on this example or not.Lengthen
R
1

I was able to come up with this solution from Misha's answer. This is working with Eclipse Luna Java compiler

Map<String, List<Item>> managersItems = itemSet
    .stream()
     .<Map.Entry<String, Item>> flatMap(item -> {
         return Stream.concat(Stream.of(item.getCreatorLogin()), item.getOwners().stream()).map(
             user -> {
                 LdapUserInfo ldapUser = LdapUserInfoFactory.create(user);
                 String manager = ldapUser.getManagerLoginName();
                 return new AbstractMap.SimpleEntry<String, Item>(manager, info);
             });
         })
    .collect(Collectors.groupingBy(Map.Entry<String, Item>::getKey,
             Collectors.mapping(Map.Entry<String, Item>::getValue,
             Collectors.toList())));
Rowan answered 13/10, 2015 at 5:29 Comment(1)
You don't need to use the curly brackets to process the inner stream. item -> Stream.concat(...).map(LdapUserInfoFactory::create).map(LdapUserInfo::getManagerLoginName).map(mgr -> new SimpleEntry<>(mgr, item))Altostratus

© 2022 - 2024 — McMap. All rights reserved.