Java 8流连接并返回多个值

时间:2016-03-22 18:21:36

标签: 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 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的代码

4 个答案:

答案 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映射到其abc值,并将结果与​​", "分开。

如果没有第三方帮助,我们可以像这样创建我们自己的组合收集器(这是基于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以获取进一步的参考。