Je me demandais si c’était une "mauvaise pratique" d’utiliser une instruction break
pour sortir d’une boucle au lieu de remplir la condition de boucle?
Je n'ai pas suffisamment d'informations sur Java et la machine virtuelle Java pour savoir comment une boucle est gérée, alors je me demandais si je ne faisais pas attention à quelque chose de critique en le faisant.
Objet de cette question: existe-t-il une surcharge de performances spécifique?
Bon Seigneur non. Il est parfois possible que quelque chose se produise dans la boucle qui satisfasse à l'exigence globale sans satisfaire à la condition de boucle logique. Dans ce cas, break
est utilisé, pour vous empêcher de faire le tour d'une boucle sans rien.
Exemple
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;
}
}
Ce qui est plus logique dans cet exemple. Continuez à boucler à 10 à chaque fois, même après l'avoir trouvée, ou à boucler jusqu'à ce que vous trouviez l'élément et que vous vous arrêtiez? Ou pour le dire en termes réels; quand vous trouvez vos clés, continuez-vous à chercher?
Modifier en réponse au commentaire
Pourquoi ne pas définir x
sur 11
pour rompre la boucle? C'est inutile. Nous avons break
! Sauf si votre code suppose que x
est définitivement plus grand que 10
plus tard (et cela ne devrait probablement pas être le cas), alors tout va bien si vous utilisez break
.
Modifier pour des raisons d'exhaustivité
Il existe certainement d'autres moyens de simuler break
. Par exemple, ajouter de la logique supplémentaire à votre condition de terminaison dans votre boucle. Dire qu'il s'agit d'une boucle inutile ou d'utiliser break
n'est pas juste. Comme indiqué, une boucle while peut souvent offrir une fonctionnalité similaire. Par exemple, en suivant l'exemple ci-dessus.
while(x < 10 && item == null)
{
if(array[x].equals("Item I am looking for"))
{
item = array[x];
}
x++;
}
Utiliser break
signifie simplement que vous pouvez obtenir cette fonctionnalité avec une boucle for
. Cela signifie également que vous n'avez pas besoin d'ajouter des conditions à votre logique de terminaison lorsque vous souhaitez que la boucle se comporte différemment. Par exemple.
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];
}
}
Plutôt qu'un while loop
avec une condition de fin ressemblant à ceci:
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"))
Utiliser break
, comme dans la plupart des autres langages, can peut être une mauvaise pratique, dans un contexte spécifique, où vous l’utilisez clairement. Mais certains idiomes très importants ne peuvent pas être codés sans cela, ou du moins auraient pour résultat un code beaucoup moins lisible. Dans ces cas, break
est la voie à suivre.
En d'autres termes, n'écoutez aucun conseil général sans réserve — concernant break
ou autre chose. Ce n'est pas une fois que j'ai vu le code totalement émacié pour imposer littéralement une "bonne pratique".
En ce qui concerne votre souci de performance, il n'y en a absolument aucune. Au niveau du bytecode, il n’existe de toute façon pas de construction de boucle explicite: tout le contrôle de flux est implémenté en termes de sauts conditionnels.
Le JLS spécifie qu'une rupture est une fin anormale d'une boucle. Cependant, ce n’est pas parce qu’elle est considérée comme anormale que de nombreux exemples de code, projets, produits, navettes spatiales, etc. ne l’utilisent pas. La spécification JVM n’indique ni existence ni absence de perte de performance, bien qu’elle soit l'exécution du code en clair continuera après la boucle.
Cependant, la lisibilité du code peut être affectée par des sauts impairs. Si vous vous en tenez à une déclaration complexe si entourée d'effets secondaires et d'un code de nettoyage étrange, avec éventuellement une rupture à plusieurs niveaux avec une étiquette (ou pire, avec un ensemble étrange de conditions de sortie les unes après les autres), cela ne se produira pas. être facile à lire pour quiconque.
Si vous voulez casser votre boucle en forçant la variable d'itération à être en dehors de la plage d'itération, ou en introduisant autrement un moyen de sortie non nécessairement direct, il est moins lisible que break
.
Cependant, boucler des temps supplémentaires de manière vide est presque toujours une mauvaise pratique, car cela prend des itérations supplémentaires et peut ne pas être clair.
À mon avis, une boucle For
devrait être utilisée lorsqu'un nombre fixe d'itérations sera effectué et qu'elles ne seront pas arrêtées avant la fin de chaque itération. Dans l’autre cas où vous voulez quitter plus tôt, je préfère utiliser une boucle While
. Même si vous lisez ces deux petits mots, cela semble plus logique. Quelques exemples:
for (int i=0;i<10;i++) {
System.out.println(i);
}
Lorsque je lirai rapidement ce code, je saurai avec certitude qu'il imprimera 10 lignes, puis continue.
for (int i=0;i<10;i++) {
if (someCondition) break;
System.out.println(i);
}
Celui-ci est déjà moins clair pour moi. Pourquoi voudriez-vous d'abord déclarer que vous allez faire 10 itérations, mais ajouter ensuite dans la boucle quelques conditions supplémentaires pour vous arrêter plus tôt?
Je préfère l'exemple précédent écrit de cette manière (même quand c'est un peu plus verbeux, mais ce n'est que avec 1 ligne de plus):
int i=0;
while (i<10 && !someCondition) {
System.out.println(i);
i++;
}
Tous ceux qui liront ce code verront immédiatement qu’il existe une condition supplémentaire susceptible de terminer la boucle plus tôt.
Bien sûr, en très petites boucles, vous pouvez toujours dire que chaque programmeur remarquera l’instruction break. Mais je peux dire de ma propre expérience que, dans les grandes boucles, ces pauses peuvent être surveillées. (Et cela nous amène à un autre sujet pour commencer à scinder le code en plus petits morceaux)
Non, ce n'est pas une mauvaise pratique de sortir d'une boucle si certaines conditions souhaitées sont atteintes (comme si une correspondance était trouvée). Plusieurs fois, vous voudrez peut-être arrêter les itérations parce que vous avez déjà réalisé ce que vous voulez, et il est inutile d’itérer davantage. Cependant, veillez à ne pas rater quelque chose par accident ou à ne pas sortir lorsque vous n’êtes pas obligé.
Cela peut également ajouter à l’amélioration des performances si vous cassez la boucle, au lieu de parcourir plusieurs milliers d’enregistrements, même si le but de la boucle est terminé (c’est-à-dire que l’enregistrement requis est déjà terminé).
Exemple :
for (int j = 0; j < type.size(); j++) {
if (condition) {
// do stuff after which you want
break; // stop further iteration
}
}
L'utilisation de boucles de rodage peut être parfaitement légitime et même constituer le seul moyen de résoudre certains problèmes.
Cependant, sa mauvaise réputation tient au fait que les nouveaux programmeurs en abusent généralement, ce qui crée une confusion dans le code, notamment en utilisant break pour arrêter la boucle dans des conditions qui auraient pu être écrites dans l’instruction de condition de boucle.
Si vous commencez à faire quelque chose comme ceci, alors je dirais que cela commence à devenir un peu étrange et que vous feriez mieux de le déplacer vers une méthode séparée qui returns
un résultat sur la condition correspondante.
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;
}
}
Pour expliquer comment nettoyer le code ci-dessus, vous pouvez le refactoriser, en le déplaçant vers une fonction qui returns
au lieu d'utiliser breaks
. C’est en général mieux s’occuper de complexe/désordre breaks
.
public boolean matches()
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(matchedCondition) {
return true;
}
}
}
return false;
}
Cependant pour quelque chose de simple comme mon exemple ci-dessous. Bien sûr, utilisez break
!
for(int i = 0; i < 10; i++) {
if(wereDoneHere()) { // we're done, break.
break;
}
}
Et si vous changiez les conditions, dans le cas ci-dessus i
et la valeur de j
, vous rendriez le code très difficile à lire. Il peut également arriver que les limites supérieures (10 dans l'exemple) soient des variables. Il serait donc encore plus difficile de deviner quelle valeur lui attribuer pour pouvoir sortir de la boucle. Vous pouvez bien sûr simplement définir i
et j
sur Integer.MAX_VALUE, mais je pense que vous pouvez voir que cela commence à devenir très compliqué. :)
Il existe un certain nombre de situations courantes pour lesquelles break
est le moyen le plus naturel d'exprimer l'algorithme. On les appelle des constructions "en boucle et demi"; l'exemple de paradigme est
while (true) {
item = stream.next();
if (item == EOF)
break;
process(item);
}
Si vous ne pouvez pas utiliser break
pour cela, vous devez vous répéter:
item = stream.next();
while (item != EOF) {
process(item);
item = stream.next();
}
Il est généralement admis que c'est pire.
De même, pour continue
, il existe un modèle commun ressemblant à ceci:
for (item in list) {
if (ignore_p(item))
continue;
if (trivial_p(item)) {
process_trivial(item);
continue;
}
process_complicated(item);
}
Ceci est souvent plus lisible que l’alternative avec else if
, surtout quand process_complicated
est plus qu’un appel de fonction.
Lectures supplémentaires: Sorties de boucle et programmation structurée: rouvrir le débat
Ce n'est pas une mauvaise pratique, mais cela peut rendre le code moins lisible. Une refactorisation utile pour contourner ce problème consiste à déplacer la boucle vers une méthode distincte, puis à utiliser une instruction return au lieu d'une pause, par exemple, comme suit (exemple extrait de la réponse de @ 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;
}
}
peut être refactorisé (en utilisant méthode d'extraction ) à ceci:
public String searchForItem(String itemIamLookingFor)
{
for(int x = 0; x < 10; x++)
{
if(array[x].equals(itemIamLookingFor))
{
return array[x];
}
}
}
Qui, lorsqu'il est appelé depuis le code environnant, peut s'avérer plus lisible.
break
et continue
cassent la lisibilité pour le lecteur, bien que cela soit souvent utile. Pas autant que le concept "goto", mais presque.
De plus, si vous prenez de nouveaux langages comme Scala (inspiré de Java et fonctionnels comme Ocaml)), vous remarquerez que break
et continue
a tout simplement disparu.
Surtout en programmation fonctionnelle, ce style de code est évité:
Pourquoi scala ne prend pas en charge la pause et continue?
Pour résumer: break
et continue
sont largement utilisés dans Java pour un style impératif, mais cela pourrait être le cas pour tous les codeurs qui pratiquaient la programmation fonctionnelle. . bizarre.
Non, ce n'est pas une mauvaise pratique. C'est le moyen le plus simple et le plus efficace.
Bien que ce ne soit pas une mauvaise pratique d’utiliser la pause et que son utilisation soit excellente, elle ne devrait pas être tout ce dont vous dépendez. Presque n'importe quelle utilisation d'une pause peut être écrite dans la condition de boucle. Le code est beaucoup plus lisible lorsque des conditions réelles sont utilisées, mais dans le cas d’une boucle longue ou infinie, les ruptures ont tout leur sens. Ils ont également un sens lors de la recherche de données, comme indiqué ci-dessus.
Si vous savez à l'avance où la boucle devra s'arrêter, la lisibilité du code sera probablement améliorée si vous énoncez la condition dans for
, while
ou `do-while
boucle.
Sinon, c'est le cas d'utilisation exact de break
.