假设我有一个数组列表,我流式传输,然后排序,然后按列表值的某些属性分组。结果映射中的列表是否会在内部排序(即在分组之前保持流的顺序)?
在文档中它说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)
不依赖于映射的顺序。