我将一段代码从.NET移植到Java,并偶然发现了一个我想使用流映射的方案。减少。
class Content
{
private String propA, propB, propC;
Content(String a, String b, String c)
{
propA = a; propB = b; propC = c;
}
public String getA() { return propA; }
public String getB() { return propB; }
public String getC() { return propC; }
}
List<Content> contentList = new ArrayList();
contentList.add(new Content("A1", "B1", "C1"));
contentList.add(new Content("A2", "B2", "C2"));
contentList.add(new Content("A3", "B3", "C3"));
我想编写一个可以流式传输contentlist内容并返回带结果
的类的函数content { propA = "A1, A2, A3", propB = "B1, B2, B3", propC = "C1, C2, C3" }
我对Java很新,所以你可能会发现一些类似于C#而不是java的代码
答案 0 :(得分:4)
您可以在reduce函数中使用适当的lambda BinaryOperator。
Content c = contentList
.stream()
.reduce((t, u) -> new Content(
t.getA() + ',' + u.getA(),
t.getB() + ',' + u.getB(),
t.getC() + ',' + u.getC())
).get();
答案 1 :(得分:4)
static Content merge(List<Content> list) {
return new Content(
list.stream().map(Content::getA).collect(Collectors.joining(", ")),
list.stream().map(Content::getB).collect(Collectors.joining(", ")),
list.stream().map(Content::getC).collect(Collectors.joining(", ")));
}
编辑:扩展Federico的内联收集器,这是一个专门用于合并内容对象的具体类:
class Merge {
public static Collector<Content, ?, Content> collector() {
return Collector.of(Merge::new, Merge::accept, Merge::combiner, Merge::finisher);
}
private StringJoiner a = new StringJoiner(", ");
private StringJoiner b = new StringJoiner(", ");
private StringJoiner c = new StringJoiner(", ");
private void accept(Content content) {
a.add(content.getA());
b.add(content.getB());
c.add(content.getC());
}
private Merge combiner(Merge second) {
a.merge(second.a);
b.merge(second.b);
c.merge(second.c);
return this;
}
private Content finisher() {
return new Content(a.toString(), b.toString(), c.toString());
}
}
用作:
Content merged = contentList.stream().collect(Merge.collector());
答案 2 :(得分:4)
处理此类任务的最通用方法是将多个收集器的结果合并为一个。
使用jOOL库,您可以拥有以下内容:
Content content =
Seq.seq(contentList)
.collect(
Collectors.mapping(Content::getA, Collectors.joining(", ")),
Collectors.mapping(Content::getB, Collectors.joining(", ")),
Collectors.mapping(Content::getC, Collectors.joining(", "))
).map(Content::new);
这将从输入列表中创建Seq
,并将3个给定的收集器组合在一起以创建Tuple3
,这只是3个值的持有者。然后使用构造函数Content
将这3个值映射到new Content(a, b, c)
。收集器本身只是将每个Content
映射到其a
,b
或c
值,并将结果与", "
分开。
如果没有第三方帮助,我们可以像这样创建我们自己的组合收集器(这是基于StreamEx pairing
收集器,它为2个收集器做同样的事情)。它需要3个收集器作为参数,并对3个收集值的结果执行修整器操作。
public interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
public static <T, A1, A2, A3, R1, R2, R3, R> Collector<T, ?, R> combining(Collector<? super T, A1, R1> c1, Collector<? super T, A2, R2> c2, Collector<? super T, A3, R3> c3, TriFunction<? super R1, ? super R2, ? super R3, ? extends R> finisher) {
final class Box<A, B, C> {
A a; B b; C c;
Box(A a, B b, C c) {
this.a = a;
this.b = b;
this.c = c;
}
}
EnumSet<Characteristics> c = EnumSet.noneOf(Characteristics.class);
c.addAll(c1.characteristics());
c.retainAll(c2.characteristics());
c.retainAll(c3.characteristics());
c.remove(Characteristics.IDENTITY_FINISH);
return Collector.of(
() -> new Box<>(c1.supplier().get(), c2.supplier().get(), c3.supplier().get()),
(acc, v) -> {
c1.accumulator().accept(acc.a, v);
c2.accumulator().accept(acc.b, v);
c3.accumulator().accept(acc.c, v);
},
(acc1, acc2) -> {
acc1.a = c1.combiner().apply(acc1.a, acc2.a);
acc1.b = c2.combiner().apply(acc1.b, acc2.b);
acc1.c = c3.combiner().apply(acc1.c, acc2.c);
return acc1;
},
acc -> finisher.apply(c1.finisher().apply(acc.a), c2.finisher().apply(acc.b), c3.finisher().apply(acc.c)),
c.toArray(new Characteristics[c.size()])
);
}
最后与
一起使用Content content = contentList.stream().collect(combining(
Collectors.mapping(Content::getA, Collectors.joining(", ")),
Collectors.mapping(Content::getB, Collectors.joining(", ")),
Collectors.mapping(Content::getC, Collectors.joining(", ")),
Content::new
));
答案 3 :(得分:3)
如果您不想在列表上进行3次迭代,或者不想创建太多Content
个中间对象,那么您需要使用你自己的实现:
public static Content collectToContent(Stream<Content> stream) {
return stream.collect(
Collector.of(
() -> new StringBuilder[] {
new StringBuilder(),
new StringBuilder(),
new StringBuilder() },
(StringBuilder[] arr, Content elem) -> {
arr[0].append(arr[0].length() == 0 ?
elem.getA() :
", " + elem.getA());
arr[1].append(arr[1].length() == 0 ?
elem.getB() :
", " + elem.getB());
arr[2].append(arr[2].length() == 0 ?
elem.getC() :
", " + elem.getC());
},
(arr1, arr2) -> {
arr1[0].append(arr1[0].length() == 0 ?
arr2[0].toString() :
arr2[0].length() == 0 ?
"" :
", " + arr2[0].toString());
arr1[1].append(arr1[1].length() == 0 ?
arr2[1].toString() :
arr2[1].length() == 0 ?
"" :
", " + arr2[1].toString());
arr1[2].append(arr1[2].length() == 0 ?
arr2[2].toString() :
arr2[2].length() == 0 ?
"" :
", " + arr2[2].toString());
return arr1;
},
arr -> new Content(
arr[0].toString(),
arr[1].toString(),
arr[2].toString())));
}
此收集器首先创建一个包含3个空StringBuilder
个对象的数组。然后定义一个累加器,将每个Content
元素的属性附加到相应的StringBuilder
。然后它定义了一个合并函数,该函数仅在流并行处理时使用,该函数合并两个先前累积的部分结果。最后,它还定义了一个finisher函数,它将3 StringBuilder
个对象转换为Content
的新实例,每个属性对应于前面步骤的累积字符串。
请检查Stream.collect()
和Collector.of()
javadocs以获取进一步的参考。