Passe para o próximo item usando o loop foreach Java 8 no fluxo


126

Eu tenho um problema com o fluxo de Java 8 foreach tentando mover para o próximo item no loop. Não consigo definir o comando como continue;, apenas return;funciona, mas você vai sair do loop neste caso. Preciso passar para o próximo item do loop. Como eu posso fazer isso?

Exemplo (não funciona):

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

Exemplo (trabalhando):

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!
}

Publique um exemplo completo, mas mínimo, para que seja mais fácil de responder.
Tunaki,

Preciso passar para o próximo item do loop.
Viktor V.

2
O ponto dele é, com o código que você mostrou, não ter continueque mover para o próximo item sem qualquer alteração funcional de qualquer maneira.
DoubleDouble de

Se o objetivo é evitar a execução de linhas após a chamada para continuar, e que você não está nos mostrando, basta colocar todas essas linhas em um elsebloco. Se não houver nada depois continue, elimine o bloco if e continue: eles são inúteis.
JB Nizet

Respostas:


278

Usar return;vai funcionar muito bem. Isso não impedirá que o loop completo seja concluído. Ele apenas interromperá a execução da iteração atual do forEachloop.

Experimente o seguinte pequeno programa:

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

Resultado:

a
c

Observe como o return;é executado para a biteração, mas cimprime bem na iteração seguinte.

Por que isso funciona?

A razão pela qual o comportamento parece não intuitivo à primeira vista é porque estamos acostumados com a returninstrução que interrompe a execução de todo o método. Portanto, neste caso, esperamos que a mainexecução do método como um todo seja interrompida.

No entanto, o que precisa ser entendido é que uma expressão lambda, como:

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

    System.out.println(str);
}

... realmente precisa ser considerado como seu "método" distinto, completamente separado do mainmétodo, apesar de estar convenientemente localizado dentro dele. Então, realmente, a returninstrução apenas interrompe a execução da expressão lambda.

A segunda coisa que precisa ser entendida é que:

stringList.stream().forEach()

... é realmente apenas um loop normal oculto que executa a expressão lambda para cada iteração.

Com esses 2 pontos em mente, o código acima pode ser reescrito da seguinte maneira equivalente (apenas para fins educacionais):

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

Com esse equivalente de código "menos mágico", o escopo da returndeclaração se torna mais aparente.


3
Já tentei desta forma e por alguma razão não funcionou, repeti este truque novamente e está funcionando. Obrigado, isso me ajuda. Parece que estou sobrecarregado =)
Viktor V.

2
Funciona como um encanto! Você poderia adicionar uma explicação de por que isso funciona, por favor?
sparkyspider

2
@Spider: adicionado.
sstan

5

Outra solução: passe por um filtro com suas condições invertidas: Exemplo:

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

pode ser substituído por

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

A abordagem mais direta parece estar usando "return;" Apesar.


2

O lambda para o qual você está passando forEach()é avaliado para cada elemento recebido do fluxo. A iteração em si não é visível de dentro do escopo do lambda, portanto, você não pode continuecomo se forEach()fosse uma macro de pré-processador C. Em vez disso, você pode ignorar condicionalmente o restante das instruções nele.


0

Você pode usar uma ifdeclaração simples em vez de continuar. Então, em vez da maneira como você tem seu código, você pode tentar:

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

O predicado na instrução if será apenas o oposto do predicado em sua if(pred) continue;instrução, então apenas use !(não lógico).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.