Неправильно ли использовать break для выхода из цикла в Java?

Мне было интересно, не использовать ли "неправильную практику" инструкцию break для выхода из цикла вместо выполнения условия цикла?

У меня недостаточно информации о Java и JVM, чтобы узнать, как обрабатывается цикл, поэтому мне было интересно, не замечал ли я что-то критическое, делая это.

В центре внимания этого вопроса: есть ли определенные накладные расходы на производительность?

Ответ 1

Хороший лорд №. Иногда существует вероятность того, что в цикле может быть что-то, что удовлетворяет общему требованию, не удовлетворяя условию логического цикла. В этом случае используется break, чтобы остановить вас круговым движением вокруг цикла.

Пример

String item;

for(int x = 0; x < 10; x++)
{
    // Linear search.
    if(array[x].equals("Item I am looking for"))
    {
       //you've found the item. Let stop.
       item = array[x];
       break; 
    }
}

Что имеет смысл в этом примере. Продолжайте цикл до 10 каждый раз, даже после того, как вы его нашли, или зациклитесь до тех пор, пока вы не найдете элемент и не остановитесь? Или поставить его в реальные условия; когда вы найдете свои ключи, вы продолжаете искать?

Изменить в ответ на комментарий

Почему бы не установить x на 11, чтобы разбить цикл? Это бессмысленно. У нас есть break! Если ваш код не делает предположение о том, что x определенно больше, чем 10 позже (и, вероятно, это не должно быть), вы прекрасно используете break.

Изменить ради полноты

Есть и другие способы моделирования break. Например, добавление дополнительной логики к вашему условию завершения в вашем цикле. Говорить, что это либо петля без смысла, либо использовать break, не справедливо. Как указывалось, цикл while может часто выполнять аналогичную функциональность. Например, следуя приведенному выше примеру.

while(x < 10 && item == null)
{
    if(array[x].equals("Item I am looking for"))
    {
        item = array[x];
    }

    x++;
}

Использование break просто означает, что вы можете достичь этой функциональности с помощью цикла for. Это также означает, что вам не нужно постоянно добавлять условия в свою логику завершения, когда вы хотите, чтобы цикл работал по-разному. Например.

for(int x = 0; x < 10; x++)
{
   if(array[x].equals("Something that will make me want to cancel"))
   {
       break;
   }
   else if(array[x].equals("Something else that will make me want to cancel"))
   {
       break;
   }
   else if(array[x].equals("This is what I want"))
   {
       item = array[x];
   }
}

Вместо while loop с условием завершения, которое выглядит следующим образом:

while(x < 10 && !array[x].equals("Something that will make me want to cancel") && 
                !array[x].equals("Something else that will make me want to cancel"))

Ответ 2

Использование break, так же как и любая другая функция языка, может быть плохой практикой в ​​конкретном контексте, где вы явно злоупотребляете им. Но некоторые очень важные идиомы не могут быть закодированы без него или, по крайней мере, приведут к гораздо менее читаемому коду. В этих случаях break - это путь.

Другими словами, не слушайте ни одного одеяла, неквалифицированных советов о break или что-нибудь еще. Не один раз я видел, что код полностью истощен, чтобы буквально обеспечить "хорошую практику".

Что касается вашей озабоченности по поводу накладных расходов, их абсолютно нет. На уровне байт-кода в любом случае нет явных конструкций цикла: все управление потоком реализовано в терминах условных переходов.

Ответ 3

JLS указывает, что разрыв - ненормальное завершение цикла. Однако только потому, что он считается ненормальным, это не означает, что он не используется во многих разных примерах кода, проектах, продуктах, космических челноках и т.д. Спецификация JVM не указывает на существование или отсутствие потери производительности, четкое выполнение кода будет продолжаться после цикла.

Однако читаемость кода может страдать от нечетных разрывов. Если вы придерживаетесь перерыва в сложном утверждении, окруженном побочными эффектами и нечетным кодом очистки, возможно, с многоуровневым разрывом с меткой (или, что еще хуже, со странным набором условий выхода один за другим), это не будет легко читаться для всех.

Если вы хотите разбить свой цикл, заставив переменную итерации находиться за пределами диапазона итераций, иначе, иначе, не обязательно-прямой способ выхода, она менее читаема, чем break.

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

Ответ 4

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

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

Пример:

for (int j = 0; j < type.size(); j++) {
        if (condition) {
            // do stuff after which you want 

            break; // stop further iteration
        }

}

Ответ 5

Использование циклов прерывания может быть совершенно законным и даже может быть единственным способом решения некоторых проблем.

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

Ответ 6

По-моему, цикл For должен использоваться, когда фиксированное количество итераций будет выполнено, и они не будут остановлены до того, как каждая итерация будет завершена. В другом случае, когда вы хотите выйти раньше, я предпочитаю использовать цикл While. Даже если вы прочтете эти два маленьких слова, это кажется более логичным. Некоторые примеры:

for (int i=0;i<10;i++) {
    System.out.println(i);
}

Когда я быстро прочитаю этот код, я точно знаю, что он напечатает 10 строк, а затем продолжит.

for (int i=0;i<10;i++) {
    if (someCondition) break;
    System.out.println(i);
}

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

Я предпочитаю предыдущий пример, написанный таким образом (даже если он немного более подробный, но только с 1 строкой больше):

int i=0;
while (i<10 && !someCondition) {
    System.out.println(i);
    i++;
}

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

Конечно, в очень маленьких циклах вы всегда можете обсудить, что каждый программист заметит оператор break. Но по собственному опыту я могу сказать, что в больших циклах эти перерывы могут контролироваться. (И это подводит нас к другой теме, чтобы начать разделение кода на более мелкие куски)

Ответ 7

Это не плохая практика, но она может сделать код менее читаемым. Один полезный рефакторинг для этого - переместить цикл в отдельный метод, а затем использовать оператор return вместо разрыва, например, это (пример снят с ответа @Chris):

String item;

for(int x = 0; x < 10; x++)
{
    // Linear search.
    if(array[x].equals("Item I am looking for"))
    {
        //you've found the item. Let stop.
        item = array[x];
        break; 
    }
}

можно реорганизовать (используя метод извлечения):

public String searchForItem(String itemIamLookingFor)
{
    for(int x = 0; x < 10; x++)
    {
        if(array[x].equals(itemIamLookingFor))
        {
            return array[x];
        }
    }
}

Что при вызове из окружающего кода может оказаться более читаемым.

Ответ 8

Если вы начнете делать что-то вроде этого, я бы сказал, что он начинает немного странным, и вам лучше переместить его на отдельный метод, который returns результат по сравнению с соглашением.

boolean matched = false;
for(int i = 0; i < 10; i++) {
    for(int j = 0; j < 10; j++) {
        if(matchedCondition) {
            matched = true;
            break;
        }
    }
    if(matched) {
        break;
    }
}

Чтобы уточнить, как очистить вышеуказанный код, вы можете реорганизовать, переведя код в функцию, которая returns вместо использования breaks. Это, в общем, лучше справляется со сложным/беспорядочным breaks.

public boolean  matches()
    for(int i = 0; i < 10; i++) {
        for(int j = 0; j < 10; j++) {
            if(matchedCondition) {
                return true;
            }
        }
    }
    return false;
}

Однако для чего-то простого, как мой пример ниже. Во что бы то ни стало используйте break!

for(int i = 0; i < 10; i++) {
    if(wereDoneHere()) { // we're done, break.
        break;
    }
}

И изменив условия, в приведенном выше случае i и j значение, вы просто сделаете код очень трудным для чтения. Также может быть случай, когда верхние пределы (10 в примере) являются переменными, поэтому было бы еще сложнее угадать, какое значение установить для того, чтобы выйти из цикла. Конечно, вы могли бы просто установить i и j в Integer.MAX_VALUE, но я думаю, вы можете видеть, что это начинает очень беспорядочно.:)

Ответ 9

Существует ряд распространенных ситуаций, для которых break является наиболее естественным способом выражения алгоритма. Они называются конструкциями "петля-и-половина"; пример парадигмы

while (true) {
    item = stream.next();
    if (item == EOF)
        break;
    process(item);
}

Если вы не можете использовать break для этого, вы должны повторить себя:

item = stream.next();
while (item != EOF) {
    process(item);
    item = stream.next();
}

Общепризнано, что это хуже.

Аналогично, для continue существует общий шаблон, который выглядит так:

for (item in list) {
    if (ignore_p(item))
        continue;
    if (trivial_p(item)) {
        process_trivial(item);
        continue;
    }
    process_complicated(item);
}

Это чаще читается, чем альтернатива цепочкой else if, особенно когда process_complicated больше, чем просто вызов функции.

Дальнейшее чтение: Loop Exits and Structured Programming: Повторное открытие дискуссии

Ответ 10

Нет, это не плохая практика. Это самый простой и эффективный способ.

Ответ 11

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

Ответ 12

Если вы заранее знаете, где цикл должен остановиться, это, вероятно, улучшит читаемость кода, чтобы указать условие в цикле for, while или `do-while.

В противном случае это точный вариант использования для break.

Ответ 13

break и continue нарушает читаемость для читателя, хотя это часто полезно. Не так много, как концепция "goto", но почти.

Кроме того, если вы возьмете несколько новых языков, например Scala (вдохновленных Java и функциональными языками программирования, такими как Ocaml), вы заметите, что break и continue просто исчезли.

Специально в функциональном программировании этот стиль кода избегается:

Почему Scala не поддерживает перерыв и продолжается?

Подводя итог: break и continue широко используются в Java для императивного стиля, но для любых кодеров, которые использовали для практического программирования, это может быть.. странно.