스트림에서 Java 8 foreach 루프를 사용하여 다음 항목으로 이동


126

루프의 다음 항목으로 이동하려는 Java 8 foreach 스트림에 문제가 있습니다. 내가 좋아하는 명령을 설정할 수 없습니다 continue;만,return; 작동하지만이 경우 루프를 종료합니다. 루프의 다음 항목으로 이동해야합니다. 어떻게 할 수 있습니까?

예 (작동하지 않음) :

try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
            filteredLines = lines.filter(...).foreach(line -> {
           ...
           if(...)
              continue; // this command doesn't working here
    });
}

예 (작동) :

try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
    filteredLines = lines.filter(...).collect(Collectors.toList());
}

for(String filteredLine : filteredLines){
   ...
   if(...)
      continue; // it's working!
}

답하기 쉽도록 완전하지만 최소한의 예제를 게시하십시오.
Tunaki

루프의 다음 항목으로 이동해야합니다.
Viktor V.

2
그의 요점은, 당신이 보여준 코드를 사용하면 continue어쨌든 기능적 변경 없이는 다음 항목으로 계속 이동할 수 없다는 것입니다.
DoubleDouble

목표가 호출 이후에 오는 행을 실행하지 않고 우리에게 보여주지 않는 것이라면이 모든 행을 else블록 에 넣으십시오 . 뒤에 아무것도 continue없으면 if 블록을 삭제하고 continue : 쓸모가 없습니다.
JB Nizet 2015 년

답변:


278

사용 return;하면 잘 작동합니다. 전체 루프가 완료되는 것을 막지는 않습니다. forEach루프 의 현재 반복 실행 만 중지됩니다 .

다음과 같은 작은 프로그램을 시도해보십시오.

public static void main(String[] args) {
    ArrayList<String> stringList = new ArrayList<>();
    stringList.add("a");
    stringList.add("b");
    stringList.add("c");

    stringList.stream().forEach(str -> {
        if (str.equals("b")) return; // only skips this iteration.

        System.out.println(str);
    });
}

산출:

a
c

return;b반복에 대해 어떻게 실행 되는지 확인하십시오 . 그러나 c다음 반복에서는 잘 인쇄됩니다.

왜 이것이 작동합니까?

처음에는 동작이 직관적이지 않은 것처럼 보이는 이유는 return전체 메서드의 실행을 방해하는 명령문에 익숙하기 때문 입니다. 따라서이 경우 main메서드 실행이 전체적으로 중지 될 것으로 예상합니다 .

그러나 이해해야 할 것은 다음과 같은 람다 표현식입니다.

str -> {
    if (str.equals("b")) return;

    System.out.println(str);
}

... 정말 그 main방법 안에 편리하게 위치해 있음에도 불구하고 방법과 완전히 분리 된 고유 한 "방법"으로 간주 되어야합니다. 따라서 return실제로이 문은 람다 식의 실행 만 중지합니다.

두 번째로 이해해야 할 사항은 다음과 같습니다.

stringList.stream().forEach()

... 실제로는 모든 반복에 대해 람다 식을 실행하는 일반적인 루프입니다.

이 두 가지 점을 염두에두고 위의 코드는 다음과 같은 방법으로 다시 작성할 수 있습니다 (교육용으로 만 해당).

public static void main(String[] args) {
    ArrayList<String> stringList = new ArrayList<>();
    stringList.add("a");
    stringList.add("b");
    stringList.add("c");

    for(String s : stringList) {
        lambdaExpressionEquivalent(s);
    }
}

private static void lambdaExpressionEquivalent(String str) {
    if (str.equals("b")) {
        return;
    }

    System.out.println(str);
}

이 "덜 마법"코드에 해당하는 코드를 사용하면 return명령문 의 범위 가 더욱 명확 해집니다.


3
나는 이미 이런 식으로 시도했고 왜 그것이 작동하지 않는지, 나는이 트릭을 다시 반복했고 작동하고 있습니다. 감사합니다. 도움이됩니다. 내가 과로 한 것
Viktor V.

2
매력처럼 작동합니다! 이것이 왜 작동하는지 설명을 추가해 주시겠습니까?
sparkyspider

2
@Spider : 추가됨.
sstan

5

또 다른 해결책 : 반전 된 조건으로 필터를 통과하십시오. 예 :

if(subscribtion.isOnce() && subscribtion.isCalled()){
                continue;
}

대체 가능

.filter(s -> !(s.isOnce() && s.isCalled()))

가장 간단한 방법은 "return"을 사용하는 것 같습니다. 그러나.


2

전달하는 람다 forEach()는 스트림에서받은 각 요소에 대해 평가됩니다. 반복 자체는 람다 범위 내에서 볼 수 없으므로 C 전 처리기 매크로 인 continue것처럼 할 수 없습니다 forEach(). 대신 나머지 문을 조건부로 건너 뛸 수 있습니다.


0

ifcontinue 대신 간단한 문을 사용할 수 있습니다 . 따라서 코드를 사용하는 대신 다음을 시도해 볼 수 있습니다.

try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
            filteredLines = lines.filter(...).foreach(line -> {
           ...
           if(!...) {
              // Code you want to run
           }
           // Once the code runs, it will continue anyway
    });
}

if 문의 술어는 if(pred) continue;명령문 의 술어와 반대 이므로 !(논리적 아님)을 사용하십시오.

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