Java 8流连接并返回多个值

我正在将一段代码从.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 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

您可以在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(); 

处理此类任务的最通用方法是将多个收集器的结果合并为一个。

使用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个值的持有者。 然后使用构造函数new Content(a, b, c)将这3个值映射到Content 。 收集器本身只是将每个Content映射到其abc值,并将结果连接在一起,用", "分隔。


如果没有第三方帮助,我们可以像这样创建我们自己的组合收集器(这是基于StreamEx pairing收集器,它为2个收集器做同样的事情)。 它需要3个收集器作为参数,并对3个收集值的结果执行修整器操作。

 public interface TriFunction { R apply(T t, U u, V v); } public static  Collector combining(Collector c1, Collector c2, Collector c3, TriFunction finisher) { final class Box { A a; B b; C c; Box(A a, B b, C c) { this.a = a; this.b = b; this.c = c; } } EnumSet 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 )); 
 static Content merge(List 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的内联收集器,这是一个专门用于合并Content对象的具体类:

 class Merge { public static Collector 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()); 

如果您不想在列表上迭代3次,或者不想创建太多的Content中间对象,那么您需要使用自己的实现来收集流:

 public static Content collectToContent(Stream 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以获取进一步的参考。

Interesting Posts