Как вырваться из вложенных циклов в Java?

1520

У меня есть такая конструкция вложенного цикла:

for (Type type : types) {
    for (Type t : types2) {
         if (some condition) {
             // Do something and break...
             break; // Breaks out of the inner loop
         }
    }
}

Теперь, как я могу вырваться из обоих циклов. Я посмотрел на подобные вопросы, но никто конкретно не касается Java. Я не мог применять эти решения, потому что большинство используемых gotos.

Я не хочу поместить внутренний цикл в другой метод.

Обновление: я не хочу повторно запускать циклы, когда я ломаюсь, я закончил выполнение цикла.

Теги:
loops

31 ответ

2052
Лучший ответ

Как и другие ответчики, я определенно предпочел бы поставить циклы другим способом, после чего вы можете просто вернуться, чтобы полностью прекратить итерацию. Этот ответ просто показывает, как можно удовлетворить требования в вопросе.

Вы можете использовать break с меткой для внешнего цикла. Например:

public class Test {
    public static void main(String[] args) {
        outerloop:
        for (int i=0; i < 5; i++) {
            for (int j=0; j < 5; j++) {
                if (i * j > 6) {
                    System.out.println("Breaking");
                    break outerloop;
                }
                System.out.println(i + " " + j);
            }
        }
        System.out.println("Done");
    }
}

Это печатает:

0 0
0 1
0 2
0 3
0 4
1 0
1 1
1 2
1 3
1 4
2 0
2 1
2 2
2 3
Breaking
Done
  • 257
    Это действительно сразу после цикла. Попытайся! Да, метка идет перед циклом, но это потому, что она маркирует цикл, а не место, в которое вы хотите выйти. (Вы можете продолжить с меткой тоже.)
  • 2
    Perl также разрешает это своей собственной системой меток. Я думаю, что многие языки это делают - меня вряд ли удивит, что это на Java.
Показать ещё 22 комментария
359

Технически правильный ответ - обозначить внешний цикл. На практике, если вы хотите выйти из любой точки внутри внутреннего цикла, вам лучше будет вытеснить код в метод (статический метод, если это необходимо), а затем вызвать его.

Это оправдало бы читаемость.

Код будет выглядеть примерно так:

private static String search(...) 
{
    for (Type type : types) {
        for (Type t : types2) {
            if (some condition) {
                // Do something and break...
                return search;
            }
        }
    }
    return null; 
}

Соответствие примера для принятого ответа:

 public class Test {
    public static void main(String[] args) {
        loop();
        System.out.println("Done");
    }

    public static void loop() {
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                if (i * j > 6) {
                    System.out.println("Breaking");
                    return;
                }
                System.out.println(i + " " + j);
            }
        }
    }
}
  • 23
    Иногда вы используете несколько локальных переменных, которые находятся вне внутреннего цикла, передавая их все может показаться неуклюжим.
  • 1
    Так как же это решение должно печатать «Готово», как в принятом ответе?
Показать ещё 3 комментария
193

Вы можете использовать именованный блок вокруг циклов:

search: {
    for (Type type : types) {
        for (Type t : types2) {
            if (some condition) {
                // Do something and break...
                break search;
            }
        }
    }
}
  • 35
    Вам не нужно создавать новый блок, чтобы использовать метку.
  • 75
    Нет, но это проясняет намерение. Смотрите первый комментарий о принятом ответе.
Показать ещё 4 комментария
121

Я никогда не использую метки. Похоже, плохая практика. Вот что я буду делать:

boolean finished = false;
for (int i = 0; i < 5 && !finished; i++) {
    for (int j = 0; j < 5; j++) {
        if (i * j > 6) {
            finished = true;
            break;
        }
    }
}
  • 4
    Разве это не должно быть && !finished вместо || !finished ? И почему тогда вообще использовать break и не использовать && !finished для внутреннего цикла?
  • 4
    Я использую break чтобы иметь возможность произвольно выйти из цикла. Если после этого есть код if block, вы можете break до его выполнения. Но ты прав насчет && . Починил это.
Показать ещё 6 комментариев
77

Вы можете использовать метки:

label1: 
for (int i = 0;;) {
    for (int g = 0;;) {
      break label1;
    }
}
  • 2
    Несправедливо, этот ответ тоже правильный. Но только 55 голосов ..
  • 1
    Это также намного проще для чтения.
Показать ещё 1 комментарий
33

возможно с функцией?

public void doSomething(List<Type> types, List<Type> types2){
  for(Type t1 : types){
    for (Type t : types2) {
      if (some condition) {
         //do something and return...
         return;
      }
    }
  }
}
15

Вы можете использовать временную переменную:

boolean outerBreak = false;
for (Type type : types) {
   if(outerBreak) break;
    for (Type t : types2) {
         if (some condition) {
             // Do something and break...
             outerBreak = true;
             break; // Breaks out of the inner loop
         }
    }
}

В зависимости от вашей функции вы также можете выйти/вернуться из внутреннего цикла:

for (Type type : types) {
    for (Type t : types2) {
         if (some condition) {
             // Do something and break...
             return;
         }
    }
}
  • 7
    Я нахожу этот путь немного загроможденным.
  • 6
    Неуклюжий и подверженный ошибкам ИМХО.
Показать ещё 1 комментарий
10

Если вам не нравятся break и goto s, вы можете использовать "традиционный" для цикла вместо "in-in" с дополнительным условием прерывания:

int a, b;
bool abort = false;
for (a = 0; a < 10 && !abort; a++) {
    for (b = 0; b < 10 && !abort; b++) {
        if (condition) {
            doSomeThing();
            abort = true;
        }
    }
}
  • 0
    Не подходит для петель foreach.
  • 0
    @JohnMcClane И вы спамите это на многих ответах на вопрос 9+ лет, потому что ...?
9

Мне нужно было сделать аналогичную вещь, но я решил не использовать расширенный цикл для этого.

int s = type.size();
for (int i = 0; i < s; i++) {
    for (int j = 0; j < t.size(); j++) {
        if (condition) {
            // do stuff after which you want 
            // to completely break out of both loops
            s = 0; // enables the _main_ loop to terminate
            break;
        }
    }
}
  • 0
    Я не считаю крутым повторять все элементы после того, как условие нарушено. Таким образом, я бы добавил перерыв в другом случае.
  • 0
    @boutta Я не уверен, как вы пришли к такому выводу. Как только условие выполнено, оба цикла завершаются.
Показать ещё 2 комментария
7

Я предпочитаю добавлять явный "выход" в тесты цикла. Это позволяет любому случайному читателю понять, что цикл может закончиться раньше.

boolean earlyExit = false;
for(int i = 0 ; i < 10 && !earlyExit; i++) {
     for(int j = 0 ; i < 10 && !earlyExit; j++) { earlyExit = true; }
}
  • 0
    Не подходит для петель foreach.
5

Решение Java 8 Stream:

List<Type> types1 = ...
List<Type> types2 = ...

types1.stream()
      .flatMap(type1 -> types2.stream().map(type2 -> new Type[]{type1, type2}))
      .filter(types -> /**some condition**/)
      .findFirst()
      .ifPresent(types -> /**do something**/);
  • 0
    Этот вопрос был задан в '09 ...
4

Скорее в течение долгого времени я думал поделиться этим типом ответа на этот вопрос.

Обычно такие случаи входят в область более значимой логики, скажем, некоторые поиски или манипулирование некоторыми из повторяющихся "для" объектов, о которых идет речь, поэтому я обычно использую функциональный подход:

public Object searching(Object[] types) {//or manipulating
    List<Object> typesReferences = new ArrayList<Object>();
    List<Object> typesReferences2 = new ArrayList<Object>();

    for (Object type : typesReferences) {
        Object o = getByCriterion(typesReferences2, type);
        if(o != null) return o; 
    }
    return null;
}
private Object getByCriterion(List<Object> typesReferences2, Object criterion) {
    for (Object typeReference : typesReferences2) {
        if(typeReference.equals(criterion)) {
             // here comes other complex or specific logic || typeReference.equals(new Object())
             return typeReference;
        }
    }
    return null;
}

Основные минусы:

  • примерно в два раза больше строк
  • больше потребления вычислительных циклов, что означает, что он медленнее от алгоритмической точки зрения
  • больше ввода текста

Профи:

  • более высокое отношение к разделению проблем из-за функциональной детализации
  • более высокий коэффициент повторного использования и контроля поиск/управление логикой без
  • методы недолговечны, поэтому они более компактны и понятнее
  • субъективно более высокое соотношение читаемости

Таким образом, это просто обработка дела с помощью другого подхода.

В основном вопрос автору этого вопроса: что вы думаете об этом подходе?

4

Вы можете отключиться от всех циклов без использования метки: и flags.

Это просто сложное решение.

Здесь условие 1 - это условие, которое используется для разрыва с петлями K и J. И условие2 - это условие, которое используется для разрыва с циклами K, J и I.

Например:

public class BreakTesting {
    public static void main(String[] args) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                for (int k = 0; k < 9; k++) {
                    if (condition1) {
                        System.out.println("Breaking from Loop K and J");
                        k = 9;
                        j = 9;
                    }
                    if (condition2) {
                        System.out.println("Breaking from Loop K, J and I");
                        k = 9;
                        j = 9;
                        i = 9;
                    }
                }
            }
        }
        System.out.println("End of I , J , K");
    }
}
  • 2
    Как бы я использовал это для циклов for-each? ;)
  • 5
    Это не сработает, если у вас более сложное условие цикла, например list.size ()> 5. И это действительно просто взломать. Трудно читать и плохая практика!
Показать ещё 1 комментарий
3

Если он внутри какой-то функции, почему бы вам просто не вернуть его:

for (Type type : types) {
    for (Type t : types2) {
         if (some condition) {
            return value;
         }
    }
}
  • 0
    Я предпочитаю эту модель. Это часто заставляло меня разбивать петли на отдельные функции. Мой код всегда был лучше после этого, поэтому мне очень нравится этот ответ.
3

Лучший и легкий способ.

outerloop:
for(int i=0; i<10; i++){
    // here we can break Outer loop by 
    break outerloop;

    innerloop:
    for(int i=0; i<10; i++){
        // here we can break innerloop by 
        break innerloop;
     }
}
  • 4
    Эти примеры взлома не очень полезны, потому что даже без ярлыка они будут ломаться в одной точке. Кроме того, всегда приятно иметь код, который вы на самом деле можете выполнить, что не относится к вашему коду, поскольку внутренняя петля никогда не может быть достигнута.
  • 0
    Я собирался напечатать то же самое. Этикетки в этом случае несколько бесполезны.
3

Довольно необычный подход, но с точки зрения длины кода (не производительности) это самая простая вещь, которую вы могли бы сделать:

for(int i = 0; i++; i < j) {
    if(wanna exit) {
        i = i + j; // if more nested, also add the 
                   // maximum value for the other loops
    }
}
3

Еще одно решение, упомянутое без примера (оно действительно работает в коде prod).

try {
    for (Type type : types) {
        for (Type t : types2) {
            if (some condition #1) {
                // Do something and break the loop.
                throw new BreakLoopException();
            }
        }
    }
}
catch (BreakLoopException e) {
    // Do something on look breaking.
}

Разумеется, BreakLoopException должен быть внутренним, частным и ускоренным без отслеживания стека:

private static class BreakLoopException extends Exception {
    @Override
    public StackTraceElement[] getStackTrace() {
        return new StackTraceElement[0];
    }
}
  • 1
    Это было упомянуто на самом деле, в ответ набрав -23 голоса ... stackoverflow.com/a/886980/2516301 . Это сделает работу, но это очень плохая практика программирования ...
  • 0
    в самом деле. однако я видел такой унаследованный код - четырехуровневые вложенные циклы с несколькими нарушающими условиями. и это было более читабельным с исключениями, а не с встроенным кодом. -23 голоса - это в основном эмоциональный рейтинг, но да - такой подход следует использовать осторожно.
Показать ещё 1 комментарий
3

Используйте ярлыки.

INNER:for(int j = 0; j < numbers.length; j++) {
    System.out.println("Even number: " + i + ", break  from INNER label");
    break INNER;
}

См. Эту статью

2

Демо для break, continue, label.

Таким образом, слова java break и continue имеют значение по умолчанию, это "ближайшая петля", Toady через несколько лет после использования Java, я просто получил ее!

Он кажется редким, но полезным.

import org.junit.Test;

/**
 * Created by cui on 17-5-4.
 */

public class BranchLabel {
    @Test
    public void test() {
        System.out.println("testBreak");
        testBreak();

        System.out.println("testBreakLabel");
        testBreakLabel();

        System.out.println("testContinue");
        testContinue();

        System.out.println("testContinueLabel");
        testContinueLabel();

    }

    /**
     testBreak
     a=0,b=0
     a=0,b=1
     a=1,b=0
     a=1,b=1
     a=2,b=0
     a=2,b=1
     a=3,b=0
     a=3,b=1
     a=4,b=0
     a=4,b=1
     */
    public void testBreak() {
        for (int a = 0; a < 5; a++) {
            for (int b = 0; b < 5; b++) {
                if (b == 2) {
                    break;
                }
                System.out.println("a=" + a + ",b=" + b);
            }
        }
    }

    /**
     testContinue
     a=0,b=0
     a=0,b=1
     a=0,b=3
     a=0,b=4
     a=1,b=0
     a=1,b=1
     a=1,b=3
     a=1,b=4
     a=2,b=0
     a=2,b=1
     a=2,b=3
     a=2,b=4
     a=3,b=0
     a=3,b=1
     a=3,b=3
     a=3,b=4
     a=4,b=0
     a=4,b=1
     a=4,b=3
     a=4,b=4
     */
    public void testContinue() {
        for (int a = 0; a < 5; a++) {
            for (int b = 0; b < 5; b++) {
                if (b == 2) {
                    continue;
                }
                System.out.println("a=" + a + ",b=" + b);
            }
        }
    }

    /**
     testBreakLabel
     a=0,b=0,c=0
     a=0,b=0,c=1
     * */
    public void testBreakLabel() {
        anyName:
        for (int a = 0; a < 5; a++) {
            for (int b = 0; b < 5; b++) {
                for (int c = 0; c < 5; c++) {
                    if (c == 2) {
                        break anyName;
                    }
                    System.out.println("a=" + a + ",b=" + b + ",c=" + c);
                }
            }
        }
    }

    /**
     testContinueLabel
     a=0,b=0,c=0
     a=0,b=0,c=1
     a=1,b=0,c=0
     a=1,b=0,c=1
     a=2,b=0,c=0
     a=2,b=0,c=1
     a=3,b=0,c=0
     a=3,b=0,c=1
     a=4,b=0,c=0
     a=4,b=0,c=1
     */
    public void testContinueLabel() {
        anyName:
        for (int a = 0; a < 5; a++) {
            for (int b = 0; b < 5; b++) {
                for (int c = 0; c < 5; c++) {
                    if (c == 2) {
                        continue anyName;
                    }
                    System.out.println("a=" + a + ",b=" + b + ",c=" + c);
                }
            }
        }
    }

}
2

Я хотел ответить на этот вопрос, но был отмечен как дубликат, который мешает мне публиковать. Поэтому отправляйте его здесь!

Если это новая реализация, вы можете попробовать переписать логику как if-else_if-else.

while(keep_going) {

    if(keep_going && condition_one_holds) {
        // code
    }
    if(keep_going && condition_two_holds) {
        // code
    }
    if(keep_going && condition_three_holds) {
        // code
    }
    if(keep_going && something_goes_really_bad) {
        keep_going=false;
    }
    if(keep_going && condition_four_holds) {
        // code
    }
    if(keep_going && condition_five_holds) {
        // code
    }   
}

В противном случае вы можете попробовать установить флаг, если это специальное условие имеет и проверьте этот флаг в каждом из ваших условий цикла.

something_bad_has_happened = false;
while(something is true && !something_bad_has_happened){
    // code, things happen
    while(something else && !something_bad_has_happened){
        // lots of code, things happens
        if(something happened){
            -> Then control should be returned ->
            something_bad_has_happened=true;
            continue;
        }
    }   
    if(something_bad_has_happened) { // things below will not be executed
        continue;
    }

    // other things may happen here as well but will not be executed
    //  once control is returned from the inner cycle
}

HERE! So, while a simple break will not work, it can be made to work using continue.

Если вы просто переносите логику с одного языка программирования на java и просто хотите получить эту работу, вы можете попробовать использовать ярлыки

2

for (int j = 0; j < 5; j++)//inner loop следует заменить на for (int j = 0; j < 5 && !exitloops; j++).

Здесь в этом случае полные вложенные циклы должны быть завершены, если условие равно True. Но если мы используем exitloops только в верхнем loop

 for (int i = 0; i < 5 && !exitloops; i++) //upper loop

Затем внутренний цикл будет продолжен, потому что нет дополнительного флага, который уведомляет этот внутренний цикл для выхода.

Пример: если i = 3 и j=2 то условие false. Но в следующей итерации внутреннего цикла j=3 условие (i*j) становится 9 которое true но внутренний цикл будет продолжаться до тех пор, пока j станет 5.

Таким образом, он должен также использовать exitloops для внутренних петель.

boolean exitloops = false;
for (int i = 0; i < 5 && !exitloops; i++) { //here should exitloops as a Conditional Statement to get out from the loops if exitloops become true. 
    for (int j = 0; j < 5 && !exitloops; j++) { //here should also use exitloops as a Conditional Statement. 
        if (i * j > 6) {
            exitloops = true;
            System.out.println("Inner loop still Continues For i * j is => "+i*j);
            break;
        }
        System.out.println(i*j);
    }
}
2

Подобно предложению @1800 INFORMATION, используйте условие, которое нарушает внутренний цикл как условие во внешнем цикле:

boolean hasAccess = false;
for (int i = 0; i < x && hasAccess == false; i++){
    for (int j = 0; j < y; j++){
        if (condition == true){
            hasAccess = true;
            break;
        }
    }
}
2
boolean broken = false; // declared outside of the loop for efficiency
for (Type type : types) {
    for (Type t : types2) {
        if (some condition) {
            broken = true;
            break;
        }
    }

    if (broken) {
        break;
    }
}
1

Вы можете сделать следующее:

  1. установить локальную переменную в false

  2. установите эту переменную true в первом цикле, когда вы хотите сломать

  3. то вы можете проверить внешний цикл, чтобы установить, будет ли условие задано, а также выйти из внешнего цикла.

    boolean isBreakNeeded = false;
    for (int i = 0; i < some.length; i++) {
        for (int j = 0; j < some.lengthasWell; j++) {
            //want to set variable if (){
            isBreakNeeded = true;
            break;
        }
    
        if (isBreakNeeded) {
            break; //will make you break from the outer loop as well
        }
    }
    
1

В некоторых случаях мы можем эффективно использовать while loop.

Random rand = new Random();
// Just an example
for (int k = 0; k < 10; ++k) {
    int count = 0;
    while (!(rand.nextInt(200) == 100)) {
       count++;
    }

    results[k] = count;
}
1

Даже создание флага для внешнего цикла и проверка того, что после каждого выполнения внутреннего цикла может быть ответом.

Как это:

for (Type type : types) {
    boolean flag=false;
    for (Type t : types2) {
        if (some condition) {
            // Do something and break...
            flag=true;
            break; // Breaks out of the inner loop
        }
    }
    if(flag)
        break;
}
0

Java не имеет функции goto, как в C++. Но все же, goto - зарезервированное ключевое слово в Java. Они могут реализовать его в будущем. Для вашего вопроса ответ заключается в том, что в Java есть что-то, что называется label, к которому вы можете применить оператор continue и break. Найдите код ниже:

public static void main(String ...args) {
    outerLoop: for(int i=0;i<10;i++) {
    for(int j=10;j>0;j--) {
        System.out.println(i+" "+j);
        if(i==j) {
            System.out.println("Condition Fulfilled");
            break outerLoop;
        }
    }
    }
    System.out.println("Got out of the outer loop");
}
0
boolean condition = false;
for (Type type : types) {
    for (int i = 0; i < otherTypes.size && !condition; i ++) {
        condition = true; // if your condition is satisfied
    }
}

Использовать условие как флаг для того, когда вы закончите обработку. Затем внутренний цикл продолжается, пока условие не выполнено. В любом случае внешняя петля будет продолжаться.

0

Вы просто используете метку для разбиения внутренних циклов

public class Test {
public static void main(String[] args) {
    outerloop:
for (int i=0; i < 5; i++) {
  for (int j=0; j < 5; j++) {
    if (i * j > 6) {
      System.out.println("Breaking");
      break outerloop;
    }
    System.out.println(i + " " + j);
  }
}
System.out.println("Done");
}
}
  • 2
    В чем отличие от принятого ответа?
-2

Убедитесь, что внутренний цикл завершен с помощью оператора if, проверив переменную внутреннего цикла. Вы также можете создать другую переменную, такую ​​как логическое, чтобы проверить, вышел ли внутренний цикл.

В этом примере он использует переменную внутреннего цикла, чтобы проверить, не было ли это:

int i, j;
for(i = 0; i < 7; i++){

for(j = 0; j < 5; j++) {

     if (some condition) {
         // Do something and break...
         break; // Breaks out of the inner loop
     }
}
     if(j < 5){    // Checks if inner loop wasn't finished
     break;    // Breaks out of the outer loop   
     } 
}
-7

Я чувствую, что использование меток делает код очень похожим на инструкцию goto. Это просто мысль. Почему бы нам не сделать исключение во внутреннем цикле for и инкапсулировать два цикла for с блоком catch try. Что-то вроде

try {
  // ...
  for(Object outerForLoop : objectsOuter) {
     // ...
     for (Object innerForLoop : objectsInner) {
        // ...
        if (isConditionTrue) 
             throw new WrappedException("With some useful message. Probably some logging as well.");
     }
  } catch (WrappedException) {
        // do something awesome or just don't do anything swallow the exception.    
  }

Просто мысль. Я предпочитаю этот код, так как он дает мне лучшую доступность (как это слово) для меня, когда это выполняется на производстве или что-то в этом роде.

  • 6
    Использование исключений для потока управления не совсем лучший метод. Так что нет, это не вариант.
  • 0
    @boutta, что заставляет тебя так думать?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню