java 如何使用java 8将一组对象分组到排序列表中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32171283/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 19:46:20  来源:igfitidea点击:

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

javasortinglambdagroupingjava-stream

提问by Ken DeLong

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

我想获取一组对象(ObjectInstance在这种情况下),我想按一个属性对它们进行分组,并将结果列表按另一个属性排序。

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 Mapwhere the keys are the domains of the ObjectInstanceobjects, 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?

上面的表达式创建了正确的数据结构: aMap其中键是ObjectInstance对象的域,值是属性列表的列表。我现在想要的是对列表进行排序,以确保它们按字母顺序排列。有没有办法在同一个表达式中做到这一点?

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

一个想法是添加.sort()之后.stream(),但真正保证工作?

回答by Jean Logeart

Use collectingAndThen:

使用collectingAndThen

List<String> beansByDomain = beans.stream()
        .collect(groupingBy( (ObjectInstance oi) -> oi.getObjectName().getDomain(),
                            mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(),
                            collectingAndThen(toList(), (l -> l.stream().sorted().collect(toList()))) )));

You can extract the Collector to make the code more readable:

您可以提取收集器以使代码更具可读性:

public static <T> Collector<T,?,List<T>> toSortedList() {
    return Collectors.collectingAndThen(Collectors.toList(), 
                                       l -> l.stream().sorted().collect(toList()));
}

 List<String> beansByDomain = beans.stream()
        .collect(groupingBy( (ObjectInstance oi) -> oi.getObjectName().getDomain(),
                            mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(),
                                    toSortedList())));

回答by Tagir Valeev

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.

请注意,我添加了该.map(ObjectInstance::getObjectName)步骤,因为您不需要来自ObjectInstance. 这会很好地工作,但我无法预测它是否比单独对每个结果列表进行排序更快。

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

如果您更喜欢单独的toSortingList()收集器(如@JeanLogeart 的回答),可以这样优化:

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.

在这里,我们明确收集到ArrayList(toList()执行相同的操作,但不能保证),然后就地对结果列表进行排序,无需额外复制(使用stream().sorted().collect(toList())您复制整个列表内容至少两次)。另请注意,<T>参数必须声明为extends Comparable<T>. 否则,您可能会错误地将此收集器用于非可比较类型,该类型可以很好地编译,但会导致运行时错误。