2016-11-21 5 views
2

私はListでいくつかのストリーム操作(特にフィルタ)を実行する関数を持っています。ストリーム操作パラメータを関数に追加するにはどうすればよいですか?

public List<String> getAndFilterNames(List<Person> people, Predicate<Person> nameFilter){ 
    List<String> allNames = people.stream() 
    .map(person -> person.getName()) 
    .filter(nameFilter) 
    .collect(Collectors.toList()); 
    return allNames; 
} 

私は中間ストリーム動作(filterdistinctなど)を渡し、私の機能は、端末操作を実行する前に、その操作を実行させることができるようにしたいです。次のようなものがあります。

public List<String> getAndProcessNames(List<Person> people, <intermediate stream operation>){ 
    List<String> allNames = people.stream() 
    .map(person -> person.getName()) 
    // perform <intermediate stream operation> here 
    .collect(Collectors.toList()); 
    return allNames; 
} 

私の現在の経験レベルでは、これは不可能です。いくつかの中間演算には(filterのような)パラメータがあり、他のものは(distinctのように)ありません。したがって、すべてのケースを扱う単一のパラメータ型を設定することはできません。 FunctionおよびSupplierを使用して計算した。

その後も、パラメータを持つ関数が変わる必要機能インタフェース... filterPredicateをとり、mapFunctionを取り、私が理解から、汎用的な機能のインターフェースを示す方法はありません。あれは正しいですか?実際に共通するクラスやインタフェースは存在しません。

public List<String> getNames(List<Person> people){ 
    List<String> allNames = people.stream() 
    .map(person -> person.getName()) 
    .collect(Collectors.toList()); 
    return allNames; 
} 

List<Person> employees = // a bunch of people 
List<String> employeeNames = getNames(employees); 
employeeNames = employeeNames.stream(). // desired operations 

EDIT

だから、最後に、私の最善の策はただmapcollect、その後のように、ケースバイケースで私の希望ストリーム動作を実行することであると思われます:または、@Holgerあたり:

public Stream<String> getNamesStream(List<Person> people){ 
    Stream<String> namesStream = people.stream() 
    .map(person -> person.getName()); 
    return namesStream; 
} 

List<Person> employees = // a bunch of people 
Stream<String> employeeNamesStream = getNamesStream(employees); 
employeeNamesStream(). // desired operations 

または、私に何かがありますか?

答えて

11

StreamからStreamまでの機能が必要です。たとえば:

public List<String> processNames(Function<Stream<String>, Stream<String>> f) { 
    return f.apply(people.stream().map(Person::getName)) 
      .collect(toList()); 
} 

は次にように呼び出す:

List<String> filteredNames = processNames(s -> s.filter(n -> n.startsWith("A"))); 
+2

したがって、唯一の「利点」は、呼び出し元が、自身の端末操作を連鎖させるのではなく、収集された 'List'を強制的に受け取ることです。関数は 'String'以外のものにマップすることはできません。私はそれをコレクション中心の視点に起因する反パターンと呼んでいます。代わりに、任意の後続操作を可能にするためにストリームを戻すだけでよいことに言及する必要があります。 – Holger

+3

OPは、「ストリーム操作」をデータとして渡す方法があるかどうかを知りたがっていました。 「代わりに単項演算子を渡す」というイディオムは、最初は明白ではなく、時には便利です。このケースでは価値があるかどうかは無関係です(そして、これが適用する適切な場所であるかどうかを吟味することは意図的にポイントを逃しているようです)。ここでのポイントは、「ストリーム操作を渡す」から「ストリームを変更するための方法を渡す」というリクエストの賢明なアップレベルがあることです。これはツールボックスに入れるのに便利なツールです。 OP(およびその他)。 –

+1

私はこの解決策を伝えることに反対しているわけではありません。私はちょうどOPにまだ起こっていないと思われる他の解決策を伝え、実際の問題を解決するかもしれません。 – Holger

0

はい、あなたは正しいです。中間的な操作が拡張する共通の基本インターフェースはありませんが、実際に実行する操作が異なるために、同じものは存在しません。

あなたいくつかのメソッドを作成することができることをあなたでしチェーンそれらのコール:例えば

private static <T> Stream<T> applyPredicates(Stream<T> input, List<Predicate<T>> predicates) { 
    Stream<T> result = Stream.concat(input, Stream.empty()); 
    for (Predicate<T> pred : predicates) { 
      result = result.filter(pred); 
    } 
    return result; 
} 

/** 
    * this could be modified to accept more then one parameters 
    */ 
private static <T, R> Stream<R> applyFunction(Stream<T> input, Function<T, R> function) { 
    return input.map(function); 
} 

そして:applyFunctionsのように多数の機能を取るために、オーバーロードされなければならないことを

List<String> list = Arrays.asList("one", "two", null, "three"); 

    Predicate<String> p1 = t -> t != null; 
    Predicate<String> p2 = t -> t.startsWith("t"); 

    applyFunction(applyPredicates(list.stream(), Arrays.asList(p1, p2)), String::length) 
      .collect(Collectors.toList()); 

お知らせ各マップ操作はストリームをTからRへ、次にYから順に変更する可能性があるため、入力パラメータを使用します。

+0

これは良いスタートでした=)うまくいけば、 –

関連する問題