java - 是否可以在单个 Stream 操作中拆分 -> 处理 -> 收集数据
问题描述
我不想将一个流分成两部分,但我想添加一个在转换数据之前拆分数据的操作。
为了说明这一点,假设我有一些常见的对象进来:
public class CommonItem {
private String name;
private boolean isSelected;
/* getters and setters */
}
我有这些来代表多种不同的事物,例如:
public class Foo {
private String text;
private boolean isChecked;
public Foo(String text, boolean isChecked) {
this.text = text;
this.isChecked = isChecked;
}
/* getters and setters */
}
和
public class Bar {
private String title;
private boolean isTicked;
public Bar(String title, boolean isTicked) {
this.title = title;
this.isTicked = isTicked;
}
/* getters and setters */
}
因此,在 Stream 操作中,我可以轻松地将它们转换为我想要的项目,并通过布尔属性将它们拆分
listOfCommonItems.stream()
.map(item -> new Foo(item.getName(), item.isSelected()))
.collect(Collectors.groupingBy(Foo::isChecked));
这会产生我想要的输出 -Map<Boolean, List<Foo>>
分成两个桶 - 那些被检查的和那些没有被检查的。但是,如果我想要相同的东西,Bar
我必须做不同的收集标准
listOfCommonItems.stream()
.map(item -> new Bar(item.getName(), item.isSelected()))
.collect(Collectors.groupingBy(Bar::isTicked));
得到Map<Boolean, List<Bar>>
.
我可以使用.filter
,但我需要处理两次流。如果我分成CommonItem
两部分并在之后处理这些结果,情况类似。而且我并不完全需要两个流,因为它是同一组数据,我只需要在两个存储桶中,其中通用标准在转换之前出现。
但是,我可以在映射之前以某种方式进行拆分,这样我就可以轻松地基于拆分的单个逻辑CommonItem
而不是最终转换项目的逻辑,然后在最后根据此标准进行收集?
解决方案
如果我理解正确,你想要这样的东西:
public static <T> Map<Boolean,List<T>> splitData(
List<CommonItem> listOfCommonItems, BiFunction<String,Boolean,T> mapper) {
return listOfCommonItems.stream()
.collect(Collectors.partitioningBy(CommonItem::isSelected,
Collectors.mapping(ci -> mapper.apply(ci.getName(), ci.isSelected()),
Collectors.toList())));
}
可用作
Map<Boolean,List<Foo>> map1 = splitData(listOfCommonItems, Foo::new);
Map<Boolean,List<Bar>> map2 = splitData(listOfCommonItems, Bar::new);
您必须了解这一点,groupingBy(Function)
或者是resppartitioningBy(Predicate)
的简写。. 因此,当您想在将元素添加到列表之前插入其他操作(例如元素)时,可以显式编写这些表单。groupingBy(Function, toList())
partitioningBy(Predicate, toList())
mapping
partitioningBy
是groupingBy
布尔键的一种特殊形式,它允许底层实现针对这种情况使用优化的代码。
要在一个 Stream 操作中执行此操作,您需要一种能够保存结果的类型。像这样的班级
class Both {
List<Foo> foos = new ArrayList<>();
List<Bar> bars = new ArrayList<>();
void add(CommonItem ci) {
String name = ci.getName();
boolean sel = ci.isSelected();
foos.add(new Foo(name, sel));
bars.add(new Bar(name, sel));
}
Both merge(Both other) {
if(foos.isEmpty()) return other;
foos.addAll(other.foos);
bars.addAll(other.bars);
return this;
}
}
你可以像收集所有的一样
Map<Boolean, Both> map = listOfCommonItems.stream()
.collect(Collectors.partitioningBy(CommonItem::isSelected,
Collector.of(Both::new, Both::add, Both::merge)));
但是,对于普通的List
,避免遍历没有任何优势,因此这只是不必要的代码复杂化。
推荐阅读
- docker - x509: 无法验证 <
> 因为它不包含任何 IP SAN - Hyperledger Fabric-CA - deepsecurity - Trend DeepSecurityManager - 适用于 AD Sync 用户的带有 DSM v11.2 的 REST API
- python - 如何将图像文件从系统复制到剪贴板
- python - Python3 表达式增量的替代方案
- javascript - 在饼图上手动添加文本
- php - 如何使用抽象类在 Symfony 中自动装配公共控制器依赖项
- sublimetext3 - 有没有办法让 sublime 3 拼写检查来检查大写字母的单词?
- mongodb - MongoDB 使 drop removeall 命令不可行?
- superpowered - 解码时在超级解码器中获取文件的当前位置/时间
- c# - 停止计时器并从其他功能重新启动它