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

Ken*_*ong 9 java sorting lambda grouping java-stream

我想获取一组对象(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() )));
Run Code Online (Sandbox Code Playgroud)

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

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

Jea*_*art 10

用途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()))) )));
Run Code Online (Sandbox Code Playgroud)

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

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())));
Run Code Online (Sandbox Code Playgroud)


Tag*_*eev 5

当然,您可以在收集之前对整个流进行排序:

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

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

如果您更喜欢单独的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;});
}
Run Code Online (Sandbox Code Playgroud)

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