19

リストまたはイテラブルは、 guavas を使用して簡単にフィルタリングできますfilter(Iterable<?> unfiltered, Class<T> type)。この操作は 2 つのタスクを実行します。リストはフィルタリングされ、指定された型 T のシーケンス変換されます。

ただし、最終的に特定の TIterables<Something<?>> のサブシーケンスを取得したい場合がよくあります。Iterables<Something<T>>

Guava が型消去のためにそのままではこの問題を解決できないことは明らかです:Something<T>はその T に関する直接的な情報を提供しません。

のようなものがあるとしましょうS<? extends Number>S<?>キャストできるかどうかを示す述語を定義できればS<Double>、それをファイラーとして使用できます。

<T extends Number> Predicate<S<?>> isOfType(Class<N> type) {...}

と:

Iterable<S<?>> numbers;
Iterable<S<?>> filtered = Iterable.filter(numbers, isOfType(Double.class));

これはフィルタリングのタスクを実行しますが、変換ステップを見逃しています。Predicate がうまく機能すると思う場合は、次のようにキャストすることを考えるかもしれません。

Iterable<S<Double>> doubles = (Iterable<S<Double>>) filtered;

しかし、これは醜いキャスト操作を暴露します。

Function<S<?>, S<Double>>別の方法として、キャストを実行する を提供することもできます。ただし、要素をキャスト(または変換)できない場合は、Class.cast()をスローするのではなくClassCastException、単に返す必要があります。nullこのようにして、明示的なキャストなしでシーケンスを変換できます。

<T extends Number> Function<S<?>, S<T>> castOrNull(Class<N> type) {...}

Iterable<S<Double>> doubles = Iterable.filter(numbers, castOrNull(Double.class));

ただし、リストは実際にはフィルタリングされていません。代わりに、に変換またはキャストできなかった各要素の null オブジェクトがまだ含まれていますS<Double>。ただし、これは次のような追加のフィルタリング手順によって簡単に解決できます。

Iterable<S<Double>> doubles = Iterables.filter(doubles, Predicates.notNull());

2 番目の解決策は、私にははるかにスマートに思えます。定義されるはFunction、キャスト (チェックされていない操作を隠す) を実行するか、必要に応じて新しいオブジェクトを実際に作成することができますS<T>

残りの質問は次のとおりです。必要な変換とフィルタリングを 1 つのステップで実行するよりスマートな方法はありますか? 次のようなユーティリティ関数を定義するだけです。

<I,O> Iterables<O> convert(
    Iterables<O> input, 
    Function<? super I, ? extends O> convert, 
    Predicate<? super O> filter);

<I,O> Iterables<O> convert(
    Iterables<O> input, 
    Function<? super I, ? extends O> convert);

Predicates.notNull()2 番目の関数は、最初の関数を;でショートカットしたものです。

しかし、述語は不要なので、最初の関数も持つ価値がありPredicates.notNull()ます。

を想像してみてくださいIterable<Iterable<? extends Number>>。コンバーター関数Function<Iterable<? extends Number>, Iterable<Double>>は、null を返す代わりに、空である可能性があるフィルター処理されたシーケンスを単に返す場合があります。追加のフィルターは、最終的に を使用して空のシーケンスを削除する場合がありますIterables.isEmpty()

4

2 に答える 2

3

この問題に対するモナドのアプローチは、 typeTのオブジェクトに対して type のオブジェクトを返す変換関数を定義することによって、 iterable を iterable の iterable に変換する操作を定義することIterable<T>です。次に、各イテラブルを連結して、単一のイテラブルを再び形成できます。このマッピングとそれに続く連結の組み合わせは、concatMapHaskell とflatMapScala で呼び出されます。

これを実装するには、最初に yourS<? extends Number>を に変換する関数を作成しますIterable<S<Double>>。これは既存の関数と非常に似ていますが、成功ケースは を含む 1 つの iterable でSあり、失敗ケース (null 状態) は空の iterable です。

<T extends Number> Function<S<?>, Iterable<S<T>>> castOrNull(Class<T> type) {
    return new Function<S<?>, Iterable<S<T>>> {
        @Override
        public Iterable<S<T>> apply(S<?> s) {
            Object contained = s.get();
            if (!(contained instanceof T)) {
                return ImmutableSet.of();
            }

            return ImmutableSet.of(new S<T>(contained));
        }
    };
}

次に、上で指定したように、これを元の iterable に適用します。

Iterable<Iterable<S<Double>>> doubleIterables = Iterables.map(numbers, castOrNull(Double.class));

次に、これらすべてを連結して、必要な値をすべて持ち、削除したい値を持たない 1 つの iterable を再度生成できます。

Iterable<S<Double>> doubles = Iterables.concat(doubleIterables);

免責事項:これをコンパイルしようとはしていません。それを機能させるには、ジェネリックをいじる必要があるかもしれません。

于 2011-09-19T23:11:52.030 に答える
2

コレクション フレームワークの Scala 言語は、Guava と同様の機能を提供します。最大で単一要素のコレクションと見なすことができる Option[T] クラスがあります。単純なフィルタリングまたは変換方法の中には、両方の操作を一度に実行する方法があります。提供された変換関数が Option クラスの値を返すことを期待しています。次に、返された Option オブジェクトの内容をコレクションにマージします。Javaでも同様の機能を実装できると思います。

最初に変換を適用してからフィルタリングするには、コレクションを2回渡す必要があるため、この問題について少し前に考えていました。その後、誰かがこのコレクションのイテレータを変換およびフィルタリングできることを教えてくれました。この場合、コレクションは 1 回トラバースされ、必要な数のフィルターと変換を適用できます。

于 2011-09-15T06:56:56.487 に答える