web-dev-qa-db-ja.com

Java 8ストリームforEachから中断または戻りますか?

外部反復 Iterableに対して使用する場合、拡張for-eachループからのbreakまたはreturnを使用します。

for (SomeObject obj : someObjects) {
   if (some_condition_met) {
      break; // or return obj
   }
}

どのようにして、 内部反復 を使ってbreakまたはreturnをJava 8のラムダ式で使用することができます。

someObjects.forEach(obj -> {
   //what to do here?
})
247
Tapas Bose

これが必要な場合は、forEachを使用しないでください。ストリームで使用可能な他の方法の1つを使用してください。どれがあなたの目標が何であるかによります。

たとえば、このループの目的が述語に一致する最初の要素を見つけることであるとします。

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findFirst();

(注:ストリームは遅延評価されるため、コレクション全体を反復することはありません。条件に一致する最初のオブジェクトで停止します)。

条件が真である要素がコレクション内にあるかどうかだけを知りたい場合は、anyMatchを使用できます。

boolean result = someObjects.stream().anyMatch(obj -> some_condition_met);
307
Jesper

このisIterable.forEach()で可能です(ただし、Stream.forEach()では確実ではありません)。解決策はニースではありませんが、それはis可能です。

WARNING:ビジネスロジックを制御するためではなく、純粋にforEach()の実行中に発生する例外的な状況を処理するために使用する必要があります。リソースが突然アクセスできなくなるなど、処理されたオブジェクトの1つが契約に違反しています(たとえば、ストリーム内のすべての要素がnullであってはならないが、突然、予期せずにその1つがnull )など.

Iterable.forEach() のドキュメントによると:

Iterableの各要素に対して指定されたアクションを実行しますuntilすべての要素が処理されるか、アクションthrows a exception...アクションによってスローされた例外は、呼び出し元にリレーされます。

そのため、すぐに内部ループを中断する例外をスローします。

コードは次のようになります-私はそれが好きだと言うことはできませんが、動作します。 BreakExceptionを拡張する独自のクラスRuntimeExceptionを作成します。

try {
    someObjects.forEach(obj -> {
        // some useful code here
        if(some_exceptional_condition_met) {
            throw new BreakException();
       }
    }
}
catch (BreakException e) {
    // here you know that your condition has been met at least once
}

try...catchはラムダ式の周りではなくではなくforEach()メソッド全体の周りにあることに注意してください。それをより見やすくするために、より明確に示す次のコードの書き起こしを参照してください。

Consumer<? super SomeObject> action = obj -> {
    // some useful code here
    if(some_exceptional_condition_met) {
        throw new BreakException();
    }
});

try {
    someObjects.forEach(action);
}
catch (BreakException e) {
    // here you know that your condition has been met at least once
}
48
Honza Zidek

ラムダでのリターンはfor-eachでの継続と同じですが、ブレークと同等のものはありません。続行するには戻るだけです。

someObjects.forEach(obj -> {
   if (some_condition_met) {
      return;
   }
})
30

以下に、私がプロジェクトで使用した解決策を示します。代わりにforEachは単にallMatchを使用します。

someObjects.allMatch(obj -> {
    return !some_condition_met;
});
20
Julian Pieles

どちらか 続けるかどうかを示す述語を使用するメソッドを使用する必要がある(代わりにブレークがある) または 例外をスローする必要がある - もちろん非常に醜いアプローチです。

それで、あなたはこのようなforEachConditionalメソッドを書くことができました:

public static <T> void forEachConditional(Iterable<T> source,
                                          Predicate<T> action) {
    for (T item : source) {
        if (!action.test(item)) {
            break;
        }
    }
}

Predicate<T>ではなく、同じ一般的な方法(Tを取り、boolを返すもの)を使用して独自の機能インターフェースを定義したい場合がありますが、期待をより明確に示す名前を使用します - ここでPredicate<T>は理想的ではありません。

11
Jon Skeet

Java 8 + rxjava を使用できます。

//import Java.util.stream.IntStream;
//import rx.Observable;

    IntStream intStream  = IntStream.range(1,10000000);
    Observable.from(() -> intStream.iterator())
            .takeWhile(n -> n < 10)
            .forEach(n-> System.out.println(n));
8
frhack

並列操作で最大限のパフォーマンスを得るには、findFirst()に似た findAny ()を使用します。

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findAny();

しかし安定した結果が望まれる場合は、代わりにfindFirst()を使用してください。

また、一致パターン(anyMatch()/ allMatch)はブール値のみを返します。一致するオブジェクトは取得されません。

5

takeWhileを使用してJava 9+で更新します。

MutableBoolean ongoing = MutableBoolean.of(true);
someobjects.stream()...takeWhile(t -> ongoing.value()).forEach(t -> {
    // doing something.
    if (...) { // want to break;
        ongoing.setFalse();
    }
});
4
user_3380739

私はこのようなことで達成しました

  private void doSomething() {
            List<Action> actions = actionRepository.findAll();
            boolean actionHasFormFields = actions.stream().anyMatch(actionHasMyFieldsPredicate());
            if (actionHasFormFields){
                context.addError(someError);
            }
        }
    }

    private Predicate<Action> actionHasMyFieldsPredicate(){
        return action -> action.getMyField1() != null;
    }
3
Mohammad Adnan

Peek(..)とanyMatch(..)を組み合わせて使うことでそれを達成できます。

あなたの例を使用して:

someObjects.stream().peek(obj -> {
   <your code here>
}).anyMatch(obj -> !<some_condition_met>);

あるいは単に汎用のutilメソッドを書くだけです。

public static <T> void streamWhile(Stream<T> stream, Predicate<? super T> predicate, Consumer<? super T> consumer) {
    stream.peek(consumer).anyMatch(predicate.negate());
}

そしてそれを次のように使います。

streamWhile(someObjects.stream(), obj -> <some_condition_met>, obj -> {
   <your code here>
});
2
tuga

これはどうですか:

final BooleanWrapper condition = new BooleanWrapper();
someObjects.forEach(obj -> {
   if (condition.ok()) {
     // YOUR CODE to control
     condition.stop();
   }
});

BooleanWrapperがフローを制御するために実装しなければならないクラスであるところ。

1
Thomas Decaux