执行stream().groupingBy维护排序的结果映射值列表?



假设我有一个数组列表,我流式传输,然后排序,然后按列表值的某些属性分组。结果映射中的列表是否会在内部排序(即在分组之前保持流的顺序)?

在文档中它说groupingBy不是一个无序的收集器,但我不确定上面是什么意思。如果不是,那么有序/无序收集器是什么意思?

arrayList.stream()
.sorted((o1, o2) -> o1.compareTo(o2))
.collect(Collectors.groupingBy(o->o.getAttribute())

当我尝试这给了我有序列表,但我需要能够保证它将始终导致有序列表,不确定它是否会打破不同的输入或使用多线程等。

groupingBy(function)groupingBy(function, toList())的简称。虽然不能保证地图条目的顺序,但toList()收集的列表将反映遇到的顺序。

虽然没有明确说明groupingBy收集器如何影响下游收集器关于顺序的操作,但文档中有关于与groupingByConcurrent(Function)的差异的注释:

对于并行流管道,combiner函数通过将一个映射中的键合并到另一个映射中来操作,这可能是一个昂贵的操作。如果不需要保留元素在生成的Map收集器中出现的顺序,则使用groupingByConcurrent(Function)可以提供更好的并行性能。

这暗示了这样一个事实,即如果下游收集器不是无序groupingBy收集器将保持该顺序,而groupingByConcurrent收集器将强制无序行为,即使下游收集器(如toList())保留该顺序。

所以下面的程序

public static void main(String[] args) {
System.out.println(isUnordered(Collectors.groupingBy(Function.identity())));
System.out.println(isUnordered(Collectors.groupingByConcurrent(Function.identity())));
}
static boolean isUnordered(Collector<?, ?, ?> c) {
return c.characteristics().contains(Collector.Characteristics.UNORDERED);
}

将打印

false
true

提醒那些没有仔细阅读的人,我们只讨论了列表中元素的顺序,而不是map中元素的顺序。

所以当我们执行下面的程序时,例如

Map<Integer, List<Integer>> m = IntStream.rangeClosed(0, 10).boxed()
.collect(Collectors.groupingBy(i -> i % 2));
System.out.println(m.keySet());
System.out.println(m.get(0));
System.out.println(m.get(1));

不能保证第一行是[0, 1]还是[1, 0],因为映射的顺序是未指定的,但我们可以确定其他两行将是

[0, 2, 4, 6, 8, 10]
[1, 3, 5, 7, 9]

作为列表有一个保证的顺序和m.get(0)/m.get(1)不依赖于映射的顺序。

最新更新