5

いくつかの入力に基づいてリストをフィルタリングする Java コードがあります。現在、ラムダを使用しています。次に例を示します。

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
    List<ComplexObject> complexObjects = retrieveAllComplexObjects();
    return complexObjects
                .stream()
                .filter( compObject -> allowedTags.contains(compObject.getTag()))
                .collect(Collectors.toList());
}

私がやりたいことは、フィルター ロジックを別のメソッドに移動して、再利用可能にし、簡単に単体テストできるようにすることです。そこで、フィルター メソッドに渡されるラムダの代わりにメソッド参照を使用したいと考えました。フィルター ロジックがかなり静的な場合 (つまり、許可されたタグのリストがコンパイル時にわかっている場合) は簡単に実行できますが、フィルター内の動的データでこれを行う方法がわかりません。

私が欲しかったのは、メソッド参照を使用してから2番目の動的パラメーターを渡す方法でした。

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
    List<ComplexObject> complexObjects = retrieveAllComplexObjects();
    return complexObjects
                .stream()
                .filter(this::filterByAllowedTags, allowedTags)
                .collect(Collectors.toList());
}

それで、私がやりたいことをすることは可能ですか、それともこの状況に間違ってアプローチしている可能性がありますか?

4

3 に答える 3

4

パラメータとして aを渡すことをお勧めしPredicateます。そうすれば、呼び出し元は、次のような任意の基準に基づいてフィルタリングできますallowedTags

public List<ComplexObject> retrieveObjectsFilteredBy(Predicate<ComplexObject> pred) {
    List<ComplexObject> complexObjects = retrieveAllComplexObjects();
    return complexObjects.stream()
        .filter(pred)
        .collect(Collectors.toList());
}

これは次のように呼び出されます。

    List<String> allowedTags = ... ;
    List<ComplexObject> result =
        retrieveObjectsFilteredBy(cobj -> allowedTags.contains(cobj.getTag()));

しかし、どれだけのリファクタリングを行うかによっては、さらに先に進むこともできます。を返す「取得」の代わりに、Listを返すようにするのはどうStreamですか? そして、retrieve-filter メソッドが を返す代わりに、Listそれも を返すようにするのはどうStreamですか?

public Stream<ComplexObject> retrieveObjectsFilteredBy2(Predicate<ComplexObject> pred) {
    Stream<ComplexObject> complexObjects = retrieveAllComplexObjects2();
    return complexObjects.filter(pred);
}

呼び出し側は次のようになります。

    List<String> allowedTags = ... ;
    List<ComplexObject> result =
        retrieveObjectsFilteredBy2(cobj -> allowedTags.contains(cobj.getTag()))
            .collect(toList());

注意深く見てみると、retrieve-filter メソッドがまったく値を追加していないことがわかるので、呼び出し元にインライン化することもできます。

    List<String> allowedTags = ... ;
    List<ComplexObject> result =
        retrieveAllComplexObjects2()
            .filter(cobj -> allowedTags.contains(cobj.getTag()))
            .collect(toList());

もちろん、呼び出し元が何をしたいのかによっては、結果をリストに収集したくない場合もあります。forEach()、または他の何かで結果を処理したい場合があります。

これで、テスト/デバッグ用にフィルターを独自のメソッドに分解でき、メソッド参照を使用できます。

boolean cobjFilter(ComplexObject cobj) {
    List<String> allowedTags = ... ;
    return allowedTags.contains(cobj.getTag());
}

    List<ComplexObject> result =
        retrieveAllComplexObjects2()
            .filter(this::cobjFilter)
            .collect(toList());

フィルターに許可されたタグを組み込みたくない場合は、フィルターを述語から代わりに述語を返す高階関数に変更できます。

Predicate<ComplexObject> cobjFilter(List<String> allowedTags) {
    return cobj -> allowedTags.contains(cobj.getTag());
}

    List<String> allowedTags = ... ;
    List<ComplexObject> result =
        retrieveAllComplexObjects2()
            .filter(cobjFilter(allowedTags))
            .collect(toList());

これらのバリエーションのどれが最も理にかなっているかは、アプリケーションがどのように見えるか、およびフィルタリングに必要なダイナミズムの種類によって異なります。

于 2014-10-29T18:38:54.607 に答える
2

メソッド参照の問題this::filterByAllowedTagsは、次のような形をしていることです。

(ComplexObject, List<String>) -> boolean

filter()しかし、形状のラムダを期待するに渡されています:

(ComplexObject) -> boolean

言い換えると、this::filterByAllowedTagsは になることはできませんが、Predicate代替インターフェースになる可能性がありますPredicate2。次に、を受け取るフィルターのオーバーロードも必要になりますPredicate2

Eclipse Collections (以前の GS Collections) には、 とselect()同じように動作するメソッドと、先ほど説明したオーバーロードのように動作するメソッドがあります。filter()selectWith()

使用select():

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags)
{
    // retrieveAllComplexObjects returns a MutableList, possibly FastList
    MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects();
    // select() returns MutableList here which extends List
    return complexObjects.select(compObject -> allowedTags.contains(compObject.getTag()));
}

使用selectWith():

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags)
{
    // retrieveAllComplexObjects returns a MutableList, possibly FastList
    MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects();
    // select() returns MutableList here which extends List
    return complexObjects.selectWith(this::filterByAllowedTags, allowedTags);
}

private boolean filterByAllowedTags(ComplexObject complexObject, List<String> allowedTags)
{
    return allowedTags.contains(complexObject.getTag());
}

注:私は Eclipse コレクションのコミッターです。

于 2014-10-30T21:42:31.897 に答える