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
映射到其a
, b
或c
值,并将结果连接在一起,用", "
分隔。
如果没有第三方帮助,我们可以像这样创建我们自己的组合收集器(这是基于StreamEx pairing
收集器,它为2个收集器做同样的事情)。 它需要3个收集器作为参数,并对3个收集值的结果执行修整器操作。
public interface TriFunction { R apply(T t, U u, V v); } public static Collector 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 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以获取进一步的参考。