2016-02-11 4 views
7

Java 8 Streamsでは、ストリームパイプラインを壊さないような方法で中間ストリーム操作をカプセル化して再利用することは可能ですか?パイプラインを壊すことなく中間ストリーム操作をカプセル化できますか?

Java Tutorial on streamsからこの例を考えてみましょう:

double average = roster 
    .stream() 
    .filter(p -> p.getGender() == Person.Sex.MALE) 
    .mapToInt(Person::getAge) 
    .average() 
    .getAsDouble(); 

は、私は私のコード全体に別の場所でフィルタとmapToInt操作を使用する必要があるとします。私が試してみて、それは例えば、再利用できるようにそのロジックをカプセル化する場合があります

IntStream maleAges(Stream<Person> stream) { 
    return stream 
     .filter(p -> p.getGender() == Person.Sex.MALE) 
     .mapToInt(Person::getAge) 
} 

これはいいですが、私は、ストリームパイプラインの混乱を作るために必要がある場合がありますそれを使用します。たとえば、Bobという男性の平均年齢を求める場合:

double averageBob = 
    maleAges(roster 
     .stream() 
     .filter(p -> "Bob".equals(p.getName())) 
    ) 
    .average() 
    .getAsDouble(); 

もっと良い方法がありますか?私はこれらの線に沿って何かを考えています。それを行うには

double averageBob = roster 
    .stream() 
    .filter(p -> "Bob".equals(p.getName())) 
    .apply(this::maleAges) // Doesn't compile 
    .average() 
    .getAsDouble(); 
+0

「int maleAges(Person p){return p.getAge();」とはどういう意味ですか? } 'を使い、' .map(this :: maleAges) 'を使う? –

+0

@LuiggiMendozaそれは '年齢'を説明していますが、 '男性'のためのものではありません:) –

+0

本質的に、私はさまざまなタイプの複数の操作をカプセル化し、それらをパイプラインに挿入する方法を探しています。 –

答えて

2

StreamExライブラリは正確にあなたの提案applyとして働くchain方法ました:

double averageBob = StreamEx.of(roster) 
     .filter(p -> "Bob".equals(p.getName())) 
     .chain(this::maleAges) // Compiles! 
     .average() 
     .getAsDouble(); 

また一つの可能​​な選択肢がmaleAgesから機能を返すことです:

Function<Stream<Person>, IntStream> maleAges() { 
    return stream -> stream 
     .filter(p -> p.getGender() == Person.Sex.MALE) 
     .mapToInt(Person::getAge); 
} 

このように使用してください:

double averageBob = StreamEx.of(roster) 
     .filter(p -> "Bob".equals(p.getName())) 
     .chain(maleAges()) // Compiles! 
     .average() 
     .getAsDouble(); 

このようにして、カプセル化された操作を簡単にパラメータ化できます。例:

Function<Stream<Person>, IntStream> agesForSex(Person.Sex sex) { 
    return stream -> stream 
     .filter(p -> p.getGender() == sex) 
     .mapToInt(Person::getAge); 
} 

double averageBob = StreamEx.of(roster) 
     .filter(p -> "Bob".equals(p.getName())) 
     .chain(agesForSex(Person.Sex.MALE)) // Compiles! 
     .average() 
     .getAsDouble(); 
+0

非常にいいです、それはまさに私が期待していたものです:) –

5

一つの方法は、単一の人を取るために、代わりにストリームを服用することで、そのようにIntStream発するでしょう:

final Predicate<Person> isMale = p -> p.getGender() == Person.Sex.MALE; 
final Function<Person, IntStream> maleAges = person -> isMale.test(person) 
      ? IntStream.of(person.age) 
      : IntStream.empty(); 
final double averageT = roster 
       .stream() 
       .flatMapToInt(maleAges) 
       .average() 
       .getAsDouble(); 

これをどこでもあなたのmaleAges関数を再利用することができます。他の機能の中での標準的なストリームAPIを強化

+0

これは素晴らしい解決策ですが、唯一の欠点は、ストリーム操作をカプセル化された関数で使用できないことです。したがって、この例では正常に動作しますが、一般的な使用ではあまりにもぎこちないかもしれません。 –

+1

私はそれを書いたときに、Personを取得してIntStreamを返すのがちょっと気になると感じました。 「ストリーム」に基づいて「インストリーム」を返すという機能を持たせるためには、もう少し「詩的な」コード(私はコードが真の詩かもしれないと信じているから)は、より自然で自然な感じです。 Joolという名前のライブラリがStreamExとほぼ同じ機能を提供していることを私の同僚と今日話しました。私の答えは、外部の依存関係に依存したくない人にとって、Java 8の明白な機能を望む人にとっては最高のものだと思います! –

+0

これはあなたが言っていることだと思いますか? https://github.com/jOOQ/joolこれはTagirのStreamExの実行可能な代替手段のようです。共有してくれてありがとう! –

関連する問題