각 Java 8 스트림에서 중단 또는 리턴?


312

사용시 외부 반복 오버 Iterable우리 사용 break또는 return피 각 루프에서와 같은 향상된 :

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

Java 8 람다 식에서 내부 반복 을 사용 break하거나 어떻게 사용할 수 있습니까?return

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

6
당신은 할 수 없습니다. 실제 for진술을 사용하십시오 .
Boann


물론 그것은 이다 가능한 forEach(). 이 솔루션은 좋은 아니지만, 그것은 이다 가능합니다. 아래 답변을 참조하십시오.
Honza Zidek

다른 접근 방식을 고려 하면 코드를 실행하지 않으려는 것이므로 간단한 if내부 조건 forEach이 트릭을 수행합니다.
Thomas Decaux

답변:


373

이것이 필요한 경우을 사용하지 말고 forEach스트림에서 사용할 수있는 다른 방법 중 하나를 사용하십시오. 어느 것이 목표인지에 달려 있습니다.

예를 들어,이 루프의 목표가 술어와 일치하는 첫 번째 요소를 찾는 것입니다.

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

(참고 : 스트림이 느리게 평가되기 때문에 전체 콜렉션을 반복하지 않습니다. 조건과 일치하는 첫 번째 오브젝트에서 중지됩니다).

컬렉션에 조건이 맞는 요소가 있는지 알고 싶다면 다음을 사용할 수 있습니다 anyMatch.

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

7
이것은 객체를 찾는 것이 목표 인 경우에 효과적 이지만, 해당 목표는 일반적으로 메소드 의 return명령문 으로 제공됩니다 findSomething. break더 일반적으로 작업 중 테이크 와 관련 이 있습니다.
Marko Topolnik

1
@MarkoTopolnik 네, 원래 포스터는 정확히 목표가 무엇인지 알기에 충분한 정보를 제공하지 않았습니다. "테이크 기간"은 제가 언급 한 두 가지 외에 세 번째 가능성입니다. (스트림으로 "테이크"하는 간단한 방법이 있습니까?).
Jesper

3
취소 동작을 올바르게 구현하는 것이 목표 인 경우는 어떻습니까? 사용자가 취소를 요청했을 때 forEach 람다 내에 런타임 예외를 던지기 위해 최선을 다할 수 있습니까?
user2163960

1
@HonzaZidek 편집되었지만 요점은 가능한지 여부가 아니라 올바른 방법으로 작업하는 것입니다. 이를 위해 강제로 사용해서는 안됩니다 forEach. 다른 적절한 방법을 대신 사용해야합니다.
Jesper

1
@Jesper 동의합니다. "예외 솔루션"이 마음에 들지 않습니다. 그러나 "이 기능은 사용할 수 없습니다"라는 문구 forEach가 기술적으로 잘못되었습니다. 또한 귀하의 솔루션을 선호하지만 답변에 제공된 솔루션이 선호되는 유스 케이스를 상상할 수 있습니다 . 실제 예외로 인해 루프를 종료 해야하는 경우. 흐름 을 제어 하기 위해 일반적으로 예외를 사용해서는 안된다는 데 동의합니다 .
Honza Zidek

53

이것은 이다 가능 Iterable.forEach()(그러나 신뢰성과 Stream.forEach()). 이 솔루션은 좋은 아니지만, 그것은 이다 가능합니다.

경고 : 비즈니스 로직을 제어하는 ​​데 사용하지 말고 순전히을 실행하는 동안 발생하는 예외 상황을 처리하는 데 사용해야합니다 forEach(). 리소스가 갑자기 액세스를 중지하는 등 처리 된 개체 중 하나가 계약을 위반하고 있습니다 (예 : 계약에 따르면 스트림의 모든 요소가 null갑자기 갑자기 예기치 않은 요소 중 하나가되어야 함 null).

에 대한 문서에 따르면 Iterable.forEach():

모든 요소가 처리되거나 조치에서 예외가 발생 Iterable 될 때까지 의 각 요소에 대해 지정된 조치를 수행합니다. 조치에 의해 발생한 예외는 호출자에게 중계됩니다.

따라서 내부 루프를 즉시 중단시키는 예외가 발생합니다.

코드는 다음과 같습니다. 나는 그것을 좋아한다고 말할 수는 없지만 작동합니다. 당신 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
}

37
나는 이것이 나쁜 습관이라고 생각하며 문제의 해결책으로 간주되어서는 안됩니다. 초보자에게는 오해의 소지가 있으므로 위험합니다. Effective Java 2nd Edition, Chapter 9, Item 57 : '예외 상황에서만 예외 사용'. 또한 '프로그램 오류를 나타 내기 위해 런타임 예외를 사용하십시오'. 결정적으로, 나는이 솔루션을 고려하는 모든 사람들이 @Jesper 솔루션을 조사 할 것을 강력히 권장합니다.
Louis F.

15
@LouisF. 나는 분명히 "나는 그것을 좋아한다고 말할 수는 없지만 작동한다"고 말했다. OP는 "forEach ()에서 벗어나는 방법"을 물었고 이것이 답입니다. 나는 이것이 비즈니스 로직을 제어하는 ​​데 사용되어서는 안된다는 것에 전적으로 동의합니다. 그러나 예외를 사용하는 것이 나쁜 습관이 아닌 forEach () 중간에 갑자기 리소스에 연결할 수없는 유용한 유스 케이스를 상상할 수 있습니다. 명확하게하기 위해 답변에 단락을 추가했습니다.
Honza Zidek 2016 년

1
나는 이것이 훌륭한 해결책이라고 생각합니다. Google에서 "자바 예외"및 "모범 사례"또는 "확인되지 않은"등과 같은 몇 가지 단어를 추가로 검색 한 후 예외 사용 방법에 대한 논란이 있습니다. 스트림에서 몇 분이 걸리는 맵을 수행했기 때문에이 솔루션을 코드에 사용했습니다. 사용자가 작업을 취소 할 수 있기를 원했기 때문에 각 계산의 시작 부분에서 "isUserCancelRequested"플래그를 확인하고 true 일 때 예외가 발생했습니다. 깨끗하고 예외 코드는 코드의 작은 부분으로 격리되어 작동합니다.
Jason

2
참고 Stream.forEach않습니다 하지 예외를 throw하는 것은이 방식으로 작동이 보장되지 않도록 예외에 대한 같은 강력한 보장을 제공 호출자에게 전달되고 Stream.forEach.
Radiodef

1
@Radiodef 올바른 지적입니다. 감사합니다. 원래 게시물은 약 Iterable.forEach()였지만 완성을 위해 텍스트에 요점을 추가했습니다.
Honza Zidek

43

람다에서의 리턴은 for-each에서의 계속과 동일하지만 휴식과 동등한 것은 없습니다. 계속하려면 반품을 수행하면됩니다.

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

2
일반적인 요구 사항을 해결하는 멋지고 관용적 인 솔루션입니다. 허용 된 답변은 요구 사항을 추정합니다.
davidxxx

6
즉이 없다 "브레이크 또는 Java 8 스트림 대해 forEach에서 복귀"실제 질문했다 않습니다
야로슬라프 Záruba

1
그래도 소스 스트림을 통해 레코드를 "풀 (pull)"합니다. 일종의 원격 데이터 세트를 통해 페이징하는 경우에는 좋지 않습니다.
Adrian Baker

23

아래에는 프로젝트에서 사용한 솔루션이 있습니다. 대신 다음 forEach을 사용하십시오 allMatch.

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

11

어느 쪽이든 당신은 (대신 휴식을 가지고 있도록) 계속할지 여부를 나타내는 술어를 사용하는 방법을 사용해야 하거나 물론, 아주 추악한 방법 - 당신은 예외를 던질 필요가있다.

따라서 다음 forEachConditional과 같은 방법을 작성할 수 있습니다 .

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

대신에 Predicate<T>, 일반적인 방법 (a T를 가져오고 리턴하는 것과 같은)을 사용하여 자신 만의 기능적 인터페이스를 정의하고 싶을 수 bool있지만 기대를보다 명확하게 나타내는 이름 Predicate<T>은 여기에 이상적이지 않습니다.


1
Java 8을 사용하는 경우 실제로이 API를 사용하는 것이 Streams API 여기에 기능적 접근 방식을 사용하는 것이 좋습니다 .
skiwi

3
이것은 고전적인 takeWhile작업이며,이 질문은 Streams API의 부족이 얼마나 많이 느껴지는지를 보여주는 것 중 하나입니다.
Marko Topolnik

2
@Marko : takeWakele은 각 항목에 대해 작업을 수행하지 않고 항목을 생성하는 작업과 같은 느낌이 듭니다. .NET의 LINQ에서는 부작용이있는 작업에 TakeWhile을 사용하는 것이 좋지 않습니다.
Jon Skeet

9

java8 + 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
Java 9는 스트림에서 takeWhile 작업을 지원합니다.
pisaruk

6

병렬 작업에서 최대 성능을 얻으 려면 findFirst ()와 유사한 findAny ()를 사용하십시오 .

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

그러나 안정적인 결과를 원하면 findFirst ()를 대신 사용하십시오.

또한 일치하는 패턴 (anyMatch () / allMatch)은 부울 만 반환하며 일치하는 객체는 얻지 않습니다.


6

다음을 사용하여 Java 9 이상으로 업데이트하십시오 takeWhile.

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

3

나는 이런 식으로 달성했다

  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

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>
});

0

이건 어때:

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

BooleanWrapper흐름을 제어하기 위해 구현해야하는 클래스는 어디에 있습니까 ?


3
아니면 AtomicBoolean?
Koekje

1
이 경우 객체 처리를 건너 뛰지 !condition.ok()forEach()어쨌든 모든 객체를 반복 하지는 않습니다 . 느린 부분이 forEach()소비자가 아닌 반복 인 경우 (예 : 느린 네트워크 연결에서 객체를 얻는 경우)이 방법은 그다지 유용하지 않습니다.
zakmck

네, 그렇습니다.이 경우 내 대답은 꽤 잘못되었습니다.
토마스 Decaux

0
int valueToMatch = 7;
Stream.of(1,2,3,4,5,6,7,8).anyMatch(val->{
   boolean isMatch = val == valueToMatch;
   if(isMatch) {
      /*Do whatever you want...*/
       System.out.println(val);
   }
   return isMatch;
});

찾기 일치하는 위치에서만 작업을 수행하고 찾기 일치 후에는 반복을 중지합니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.