2016-03-22 7 views
7

.NETからJavaにコードを移植して、ストリームをマップに使用して&を減らすシナリオを見つけました。Java 8ストリームが複数の値を結合して返す

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の内容によってストリームとJavaに私はかなり新しいんだ結果

content { propA = "A1, A2, A3", propB = "B1, B2, B3", propC = "C1, C2, C3" } 

を持つクラスを返すことができる機能を書きたいですC#のようなJavaより

+1

良い問題の説明とそれを使用しています。しかし今、あなたは何を試しましたか? StackOverflowは他の人にコードを書くことを可能にするコミュニティではありません。むしろ、あなたが試したもの、入手したもの、期待したことを示す[mcve]を投稿する必要があります。また、StackOverflowコミュニティは、あなたが間違っていた場所を特定し、解決策を指摘するのに役立つかもしれません。 – AJNeufeld

+0

@AJNeufeldの提案に感謝します。これは私が毎日それを使用していますが、それについての質問を投稿しようとする初めてのことです。私は、基本的なforループを使って、コンテンツを追加する変数を持っています。そしてストリームのforEachを使用してみたところ、Javaの匿名関数はforeachブロック内の変数を変更できないことに気付きました。また、私は手の届くところに手紙を書こうと思っていたが、効率的ではないと気づいた。 – Vedanth

答えて

3

reduce関数でBinaryOperatorに適切なラムダを使用できます。

Content c = contentList 
      .stream() 
      .reduce((t, u) -> new Content(
            t.getA() + ',' + u.getA(), 
            t.getB() + ',' + u.getB(), 
            t.getC() + ',' + u.getC()) 
        ).get(); 
+1

これは動作しますが、多くの一時的な 'Content'オブジェクトが作成されます。 – AJNeufeld

+1

@AJNeufeldは同意しましたが、それはスペースと時間の選択です。 – mks

+1

3つのプロパティしかないので、実装には部分的です。しかし、より一般的なケースでは、より多くのプロパティ、より複雑なプロパティ、プライベートプロパティなどがあります。最も良いのは、reduce((t、u) - > new Content(t、u);特殊なコンストラクタを持つ 'Content'オブジェクト自体、あるいは情報を効率的に蓄積する' ContentCollector'を実際に記述しています。 – AJNeufeld

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(", "))); 
} 

EDIT:フェデリコのインラインコレクタを拡張し、ここでは、コンテンツオブジェクトをマージする専用の具象クラスです:

として使用
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()); 
3

の場合あなたはlisに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に追加するアキュムレータを定義します。次に、ストリームが並行して処理されるときにのみ使用されるマージ関数を定義します。マージ関数は、以前に累積された2つの部分結果をマージします。最後に、3つのStringBuilderオブジェクトをContentの新しいインスタンスに変換するフィニッシャ関数も定義します。各プロパティは、前の手順の累積された文字列に対応しています。

詳細については、Stream.collect()Collector.of() javadocsを確認してください。

4

このようなタスクを処理する最も一般的な方法は、複数のコレクタの結果を1つにまとめることです。 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つの与えられたコレクターを兼ね備えています。これら3つの値は、コンストラクタnew Content(a, b, c)を使用してContentにマッピングされます。コレクタ自身は、それぞれContenta,bまたはcの値にマッピングし、結果を一緒に結合することを", "で区切ります。


サードパーティの助けがなければ、我々は(これは2人のコレクターのために同じことをStreamExpairingコレクタの基づいています)、このような当社独自のコンバイナコレクタを作成することができます。引数として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 
)); 
関連する問題