Чи погана практика використовувати break для виходу з циклу в Java? [зачинено]


79

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

У мене недостатньо розуміння Java та JVM, щоб знати, як обробляється цикл, тому мені було цікаво, чи не пропускаю я щось критичне, роблячи це.

Фокус цього питання: чи є конкретні накладні витрати?


13
я не думаю, що це погана практика, оскільки "перерви призначені лише для цього
Android Killer


2
особисто я думаю, що це залежить від ситуації, іноді використання break може підвищити читабельність коду, і я не думаю, що в таких ситуаціях це можна назвати поганою практикою
user902383

2
Це вважається поганою практикою, оскільки може призвести до багатьох небажаних ситуацій, таких як вихід із циклу / методу після створення потоку, а не його закриття. Але якщо ви використовуєте його обережно, це нормально.
Пшемо

3
В основному, ви можете замінити будь- breakякий ifприблизно на весь інший код. Ось як це робиться в деяких інших мовах, які з фашизмом дотримуються парадигми "структурованого програмування". Код виглядає жахливо.
Marko Topolnik

Відповіді:


134

Добрий володар ні. Іноді існує ймовірність того, що в циклі може статися щось, що задовольняє загальну вимогу, не задовольняючи умови логічного циклу. У такому випадку 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's 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"))

OP також запитав про щось подібне до встановлення x до 1000, щоб розірвати петлю /
nanofarad

5
Як щодо циклу while замість перерви? Тоді всі умови завершення циклу знаходяться безпосередньо на початку циклу. while(x < 10 && item == null)якщо вам до душі, використовуйте ітератор.
Freiheit

@Freiheit: ви можете ним користуватися або навіть покращувати. Але я думаю, що приклад, наведений, Chrisпризначений для розуміння OP.
Нандкумар Текале

1
@hexafraction: що робити, якщо я хочу використовувати xіндекс у коді нижче циклу? напр.found string at index x
Нандкумар Текале

@NandkumarTekale Тоді вам потрібно буде оголосити це зовні і вирватися з циклу з ним цілим.
нанофарад

18

Використання break, як практично будь-якої іншої мовної функції, може бути поганою практикою в певному контексті, коли ви явно зловживаєте нею. Але деякі дуже важливі ідіоми не можуть бути закодовані без нього, або, принаймні, це призведе до набагато менш читабельного коду. У цих випадках breakце шлях.

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

Що стосується Вашого занепокоєння з приводу накладних показників ефективності, то тут немає абсолютно жодного. На рівні байт-коду і так не існує явних конструкцій циклу: весь контроль потоку реалізований з точки зору умовних стрибків.


6

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

Однак читабельність коду може постраждати з непарними перервами. Якщо ви тримаєте розрив у комплексі if, оточений побічними ефектами і непарним кодом очищення, можливо, багаторівневим розривом з міткою (або ще гірше, з дивним набором умов виходу один за одним), це не буде читати будь-кого.

Якщо ви хочете розірвати цикл, примусивши змінну ітерації виходити за межі діапазону ітерацій або вводячи інакше не обов'язково прямий спосіб виходу, це менше читається, ніж break.

Однак повторення зайвих разів порожнім способом майже завжди є поганою практикою, оскільки вимагає додаткових ітерацій і може бути незрозумілим.


5

На мій погляд, 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. Але я можу сказати на власному досвіді, що у більших циклах ці розриви можна контролювати. (І це підводить нас до іншої теми, щоб розпочати розподіл коду меншими шматками)


2
Це не враховує ситуацію, коли умова досягається ПІД ЧАС циклу for або while. Якщо цикл for / while викликає умову (або, можливо, щось виконується в іншому потоці), можливо, вам доведеться зламати раніше. Цикл while, який ви написали, передбачає, що вам потрібно перевірити стан лише на початку ітерації.
Doc

Я повністю з цим згоден. Якщо у вас є цикл for, намір полягає в тому, щоб логіка ініціалізації, умови та приросту були в одному місці. Це економить необхідність шукати тут і скрізь, щоб знайти саме те, що може відбуватися. Якщо ви не знаєте, скільки разів ви хочете повторити цикл, тоді ви циклите щось, поки ", тому це повинен бути цикл while. Якщо ви циклюєтеся до чогось, це повинно робити деякий час. Я взагалі не припускаю, що перерви - це погана практика, але для них є час і місце.
ThePerson

4

Використання обриву циклів може бути цілком законним, і це може бути єдиним способом вирішення деяких проблем.

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


Я бачу це занадто часто. Я забиваю кожи кожен раз, коли код see, який ініціалізує циклічні змінні, відкриває цикл while(true), а потім breaks за певної умови.
Джон

@ John, хоча іноді зловживають, в цьому немає нічого поганого, деякі ідіоми вимагають, щоб while (true) ... зламався. Наприклад, цикли подій, де умова виходу не відповідає умовному умові, і вам може знадобитися виконати різну обробку залежно від сигналу перед тим, як зламати.
аварія

4

Ні, це не погана практика вирватися з циклу, коли, якщо досягнуто певної бажаної умови (як знайдено збіг). Багато разів вам може знадобитися зупинити ітерації, оскільки ви вже досягли того, що хочете, і немає сенсу повторювати далі. Але будьте обережні, щоб переконатися, що ви випадково чогось не пропустили або не прорвались, коли це не потрібно.

Це також може сприяти підвищенню продуктивності, якщо ви перериваєте цикл, замість того, щоб повторювати тисячі записів, навіть якщо мета циклу завершена (тобто може відповідати необхідному запису, який вже зроблено).

Приклад:

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

            break; // stop further iteration
        }

}

Ви можете скористатися міченим розривом для завершення двох петель:search: for (int i = 0; i < s; i++) { for (int j = 0; j < t.size(); j++) { if (condition) { break search; } } }
Michael Konietzka

так, я це вже знаю. Я випадково опублікував його.
Ankur Shanbhag

2

Це не погана практика, але це може зробити код менш читабельним. Одним з корисних рефакторингів, щоб обійти це, є переміщення циклу до окремого методу, а потім використання оператора 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's 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];
        }
    }
}

Що при виклику з навколишнього коду може виявитись більш читабельним.


2

Якщо ви починаєте робити щось подібне, то я б сказав, що це починає ставати трохи дивним, і вам краще перенести його на окремий метод, який 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, але я думаю , що ви можете бачити це починає заплутатися дуже швидко. :)


Рефакторинг "окремого методу" заслуговував на увагу. Якщо ви стурбовані тим, що розділ коду стає важко прочитати з будь-якої причини, витяг його у свій власний метод може допомогти. У контексті цього питання він також може перетворити breakвисловлювання на returnтвердження.
Рассел Сільва

1
@RussellSilva Дякую, я відредагую, щоб додати акцент.
Кенні Кейсон,

Ваш перший випадок можна спростити, перервавши ярлик. Деяким людям це не подобається, тому що це схоже на goto, але це один із випадків, коли це особливо корисно.
Даррел Хоффман

@DarrelHoffman Ви могли б це зробити, але я думаю, що перетворення його на окремий метод та повернення є більш "стандартним" способом обробки цього, принаймні в Java.
Кенні Кейсон,

2

Існує ряд типових ситуацій, для яких 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це більше, ніж просто один виклик функції.

Подальше читання: Виходи з циклу та структуроване програмування: відновлення дебатів


1

Ні, це не погана практика. Це найпростіший та найефективніший спосіб.


4
Але це також може бути найменш читабельним способом. Такі загальні твердження, як правило, помилкові у значній частині випадків.
Stephen C

Це залежить від вашого logic. Іноді ви хочете аномально завершити цикл на основі якогось прапора чи чогось іншого. У такому випадку breakце хороший варіант.
user2550754

1

Незважаючи на неблагополучну практику використання перерви, і для неї існує багато чудових способів використання, це не повинно бути все, на що ви покладаєтесь. Майже будь-яке використання перерви може бути записано в умову циклу. Код набагато зручніший для читання, коли використовуються реальні умови, але у випадку тривалого або нескінченного циклу розриви мають цілковитий сенс. Вони також мають сенс при пошуку даних, як показано вище.


1

Якщо ви заздалегідь знаєте , де цикл буде зупинити, то, ймовірно , поліпшити читаність коду , щоб вказати умову в for, whileабо `do-whileпетлі.

В іншому випадку це саме той варіант використання для break.


1

breakі continueпорушує читабельність читача, хоча це часто корисно. Не так сильно, як поняття "goto", але майже.

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

Особливо у функціональному програмуванні уникають такого стилю коду:

Чому Scala не підтримує перерву та продовження?

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.