我不想将流分成两部分,但是我想添加一个操作,将数据转换为之前。
为了说明这一点,假设我有一些通用对象进来:
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操作中,我可以轻松地将它们转换为所需的项目,并通过boolean属性将它们拆分
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
进行分割,而不必为最终转换后的项分配一个逻辑,然后最后根据这个标准收集吗?
答案 0 :(得分:3)
如果我正确理解了您,您会想要这样的东西:
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)
或partitioningBy(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
,避免遍历没有任何优势,因此这将是不必要的代码复杂化。
答案 1 :(得分:0)
这是我的两分钱。
List<CommonItem> listOfCommonItems = Arrays.asList(
new CommonItem("foo", true),
new CommonItem("bar", false),
new CommonItem("bar", false),
new CommonItem("foo", true),
new CommonItem("foo", false),
new CommonItem("bar", true),
new CommonItem("bar", false)
);
Map<Class<?>, ? extends Map<Boolean, ? extends List<?>>> map = listOfCommonItems.stream()
.map(item -> new SimpleEntry<>(item.isSelected(), convertCommonItem(item)))
.collect(groupingBy(t -> t.getValue().getClass(), partitioningBy(
Entry::getKey, mapping(t -> t.getValue(), toList()))));
System.out.println(map);
每个类(Foo
,Bar
,...)映射到Partition
对象,该对象是一个包含键 true 和 false 。现在,该分区的每个值都包含一个列表,其中包含boolean属性相同的对象。
就像您在注释中所称的那样,用于存储分区的标签只是通过将存储分区标签(布尔值)和转换后的对象包装成一对(在我的情况下为Map
)中来实现的。 / p>
这是转换的一些虚拟实现。我不完全知道您打算如何将常见项目转换为它们各自的类,但这是一个提供一些上下文的简单实现:
AbstractMap.SimpleEntry