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

时间:2015-08-23 20:37:14

标签: 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() )));

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

一个想法是在.sort()之后立即添加.stream(),但这是否真的有效?

2 个答案:

答案 0 :(得分:9)

使用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()))) )));

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

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())));

答案 1 :(得分:3)

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

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

请注意,我添加了.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;});
}

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