Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to group a set of objects into sorted lists using java 8?

I want to take a set of objects (ObjectInstance in this case), and I want to group them by one property, and have the resulting lists be sorted on another.

Set<ObjectInstance> beans = server.queryMBeans(null, null);
Map<String, List<String>> beansByDomain = beans.stream()
            .collect(groupingBy( (ObjectInstance oi) -> oi.getObjectName().getDomain(),
                                mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(),
                                toList() )));

The above expression creates the correct data structure: a Map where the keys are the domains of the ObjectInstance objects, and the values are Lists of the property lists. What I want is to now sort the Lists, to make sure they are in alphabetical order. Is there some way to do this in the same expression?

One idea would be to add .sort() right after .stream(), but is that really guaranteed to work?

like image 823
Ken DeLong Avatar asked Aug 23 '15 20:08

Ken DeLong


1 Answers

Surely you can sort the whole stream before collecting:

Map<String, List<String>> beansByDomain = beans.stream()
        .map(ObjectInstance::getObjectName)
        .sorted(Comparator.comparing(ObjectName::getCanonicalKeyPropertyListString))
        .collect(groupingBy(ObjectName::getDomain,
                            mapping(ObjectName::getCanonicalKeyPropertyListString,
                            toList() )));

Note that I added the .map(ObjectInstance::getObjectName) step as you don't need anything else from ObjectInstance. This will work nicely, though I cannot predict whether it's faster than sorting each resulting list separately or not.

If you prefer the separate toSortingList() collector (as in @JeanLogeart answer), it can be optimized like this:

public static <T extends Comparable<T>> Collector<T,?,List<T>> toSortedList() {
    return collectingAndThen(toCollection(ArrayList::new),
                    (List<T> l) -> {l.sort(Comparator.naturalOrder()); return l;});
}

Here we explicitly collect to ArrayList (toList() does the same, but it's not guaranteed), then sort the resulting list in-place without additional copying (using stream().sorted().collect(toList()) you copy the whole list content at least twice). Also note that <T> parameter must be declared as extends Comparable<T>. Otherwise you can mistakenly use this collector for non-comparable type which would compile fine, but result in runtime error.

like image 117
Tagir Valeev Avatar answered Sep 28 '22 11:09

Tagir Valeev