web-dev-qa-db-ja.com

述語で最初の要素を探す

Java 8のラムダを使い始めたばかりで、関数言語で慣れ親しんだものをいくつか実装しようとしています。

たとえば、ほとんどの関数型言語には、シーケンスを操作するある種のfind関数や、最初の要素を返すリストがあり、その述語はtrueです。 Java 8でこれを達成するために私が見ることができる唯一の方法は以下のとおりです。

lst.stream()
    .filter(x -> x > 5)
    .findFirst()

しかし、これは私にとっては非効率的に思えます。少なくとも私の知る限りでは、フィルタがリスト全体をスキャンするためです(これは間違っている可能性があります)。もっと良い方法はありますか?

428
siki

いいえ、フィルタはストリーム全体をスキャンしません。これは中間操作であり、遅延ストリームを返します(実際にはすべての中間操作が遅延ストリームを返します)。あなたを納得させるために、あなたは単に次のテストを行うことができます。

List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
int a = list.stream()
            .peek(num -> System.out.println("will filter " + num))
            .filter(x -> x > 5)
            .findFirst()
            .get();
System.out.println(a);

どの出力:

will filter 1
will filter 10
10

ストリームの最初の2つの要素だけが実際に処理されているのがわかります。

だからあなたは完璧にうまくいっているあなたのアプローチで行くことができます。

624
Alexis C.

しかし、フィルタはリスト全体をスキャンするので、これは私にとって非効率的なようです。

いいえ、そうではありません - 述語を満たす最初の要素が見つかるとすぐに "中断"します。 streamパッケージのjavadoc で、特に怠惰についての詳細を読むことができます(私の強調点)。

フィルタリング、マッピング、重複削除などの多くのストリーム操作を遅延して実装することができ、最適化の機会が得られます。たとえば、「3つの連続する母音で最初の文字列を見つける」と入力文字列をすべて調べる必要はありません。ストリーム操作は、中間(ストリーム生成)操作と最終(値または副作用生成)操作に分けられます。中間操作は常に遅延します。

96
wha'eve'
return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().orElse(null);

オブジェクトのリストから1つのオブジェクトだけを除外しなければなりませんでした。だから私はこれを使った、それが役立つことを願っています。

28
CodeShadow

Alexis C の答えに加えて、あなたが探している要素が存在するかどうかわからない配列リストを使っているのなら、これを使ってください。

Integer a = list.stream()
                .peek(num -> System.out.println("will filter " + num))
                .filter(x -> x > 5)
                .findFirst()
                .orElse(null);

それから、 a nullであるかどうかを簡単に確認できます。

11

あなたがブール値の戻り値を探しているならば、我々はnullチェックを追加することによってそれをよりよくすることができます:

return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().orElse(null) != null;
0
shreedhar bhat