Java8流连接并返回多个值
我正在将一段代码从.NET移植到Java,无意中发现了一个场景,在这个场景中,我想使用流来映射和减少Java8流连接并返回多个值,java,dictionary,java-8,java-stream,collect,Java,Dictionary,Java 8,Java Stream,Collect,我正在将一段代码从.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 get
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"));
我对Java相当陌生,因此您可能会发现一些代码更像C#而不是Java您可以使用适当的lambda来实现in-reduce函数
Content c = contentList
.stream()
.reduce((t, u) -> new Content(
t.getA() + ',' + u.getA(),
t.getB() + ',' + u.getB(),
t.getC() + ',' + u.getC())
).get();
如果您不想在列表上重复3次,或者不想创建太多的
内容
中间对象,则需要使用自己的实现收集流:
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())));
}
公共静态内容收集器内容(流){
返回流。收集(
收藏(
()->新的StringBuilder[]{
新建StringBuilder(),
新建StringBuilder(),
新的StringBuilder(),
(StringBuilder[]arr,内容元素)->{
arr[0]。追加(arr[0]。长度()==0?
elem.getA():
“,”+elem.getA());
arr[1]。追加(arr[1]。长度()==0?
elem.getB():
“,”+elem.getB());
arr[2]。追加(arr[2]。长度()==0?
elem.getC():
“,”+elem.getC());
},
(arr1,arr2)->{
arr1[0]。追加(arr1[0]。长度()==0?
arr2[0]。toString():
arr2[0]。长度()==0?
"" :
“,”+arr2[0].toString());
arr1[1]。追加(arr1[1]。长度()==0?
arr2[1]。toString():
arr2[1]。长度()==0?
"" :
“,”+arr2[1].toString());
arr1[2]。追加(arr1[2]。长度()==0?
arr2[2]。toString():
arr2[2]。长度()==0?
"" :
“,”+arr2[2].toString());
返回arr1;
},
arr->新内容(
arr[0]。toString(),
arr[1]。toString(),
arr[2].toString();
}
此收集器首先创建一个包含3个空StringBuilder
对象的数组。然后定义一个累加器,将每个内容
元素的属性附加到相应的StringBuilder
。然后它定义了一个合并函数,该函数仅在并行处理流时使用,该函数合并两个先前累积的部分结果。最后,它还定义了一个finisher函数,用于将3个StringBuilder
对象转换为Content
的新实例,每个属性对应于前面步骤中累积的字符串
请查看和javadocs以获得进一步的参考。处理此类任务的最通用方法是将多个收集器的结果合并到一个收集器中 使用该库,您可以获得以下内容:
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个值的保持器。然后使用构造函数新内容(a、b、c)
将这三个值映射到内容中。收集器本身只是将每个内容
映射到其a
、b
或c
值中,并将结果连接在一起,以分隔,“
”
在没有第三方帮助的情况下,我们可以像这样创建自己的组合器收集器(这是基于收集器的,它对2个收集器执行相同的操作)。它接受3个收集器作为参数,并对3个收集的值的结果执行finisher操作
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()])
);
}
很好的问题描述。但是现在,你尝试了什么?StackOverflow不是一个让其他人为您编写代码的社区。相反,你需要发布一个展示你尝试了什么、得到了什么、期望了什么的帖子,StackOverflow社区可能会试图帮助你确定哪里出了问题,并指出解决方案。@AJNeufeld感谢你的建议。这也是我第一次尝试在网站上发布问题,尽管我每天都在使用它。我让它使用一个基本for循环,使用一个变量来附加内容。我尝试在流上使用forEach,然后意识到Java匿名函数不允许修改forEach块内的变量。我还打算为reach one编写一个collect,但意识到它也不会有效率。@AJNeufeld同意,但这是空间和时间之间的选择。由于只有3个属性,我偏爱我的实现。但是在更一般的情况下——有更多的属性、更复杂的属性、私有属性等等——最好是reduce((t,u)->新内容(t,u);
使用专门的构造函数将合并委托给内容
对象本身,或者实际编写一个ContentCollector
,有效地积累信息。
Content merged = contentList.stream().collect(Merge.collector());
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())));
}
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);
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
));