以下摘录自 the Java tutorials in aggregate operations ,我们将人员的姓名映射到他们的性别。
Map<Person.Sex, List<String>> namesByGender =
roster
.stream()
.collect(
Collectors.groupingBy(
Person::getGender,
Collectors.mapping(
Person::getName,
Collectors.toList())));
我了解收集操作:
1) 根据 getGender 的结果对流中的每个 Person 进行分组。
2) 将每个Person映射到getName的结果。
3) 根据结果形成一个列表。
4) 生成一个Map,其键为人员性别,数据值为人员姓名。
我的问题是:
1) 收集者按什么顺序行动?
2)它们之间的间歇类型有哪些?
最佳答案
收集者不会“行动”,因此不会按照命令行动。这不像一个收集器会处理所有数据,然后使用间歇类型将其传递给另一个收集器。
您正在使用这些工厂组合一个收集器,它将在传递给Stream.collect
时立即完成整个工作。
作为documentation of the Collector
interface解释:
A Collector is specified by four functions that work together to accumulate entries into a mutable result container, and optionally perform a final transform on the result. They are:
- creation of a new result container (supplier())
- incorporating a new data element into a result container (accumulator())
- combining two result containers into one (combiner())
- performing an optional final transform on the container (finisher())
因此,toList()
收集器可以像 ArrayList::new
作为供应商、List::add
作为累加器一样简单地实现List::addAll
作为组合器,不需要自定义完成器,这确实是在引用实现中实现的方式,但这是一个实现细节,允许其他实现。
然后,Collectors.mapping
使用指定的下游收集器构造一个新的收集器,并通过首先应用指定的映射器函数并将其结果传递给原始累加器函数来装饰它的累加器函数。结果又是一个由四个函数组成的收集器。在收集操作期间,映射器函数将在添加到列表之前应用于每个元素。
最后,Collectors.groupingBy
将进行更复杂的构图。供应商将创建一个新的映射,通常是HashMap
。累加器将评估您的分组函数并将结果存储到映射中,使用诸如 Map.computeIfAbsent 之类的操作,如果 key 是新的,则该操作将评估下游收集器的供应商,然后通过应用下游收集器的累加器函数,这是您场景中的组合函数。如果只有一个映射包含键,则组合器函数使用任一映射的值确定Map.merge
;如果两个映射中都存在键,则使用下游的组合器。 p>
因此,组合收集器的处理包括对指定函数的交错处理,而不是一个接一个地处理收集器。换句话说,对于顺序执行,操作将相当于
Map<Person.Sex, List<String>> namesByGender = new HashMap<>();
for(Person p: roster)
namesByGender.computeIfAbsent(p.getGender(), k -> new ArrayList()).add(p.getName());
关于java - Java 流上的聚合操作如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47125135/