简体   繁体   中英

How to write conditions in loop bodies?

This is probably a bit subjective, but I'm in fact looking for answers that contain some reasoning.

I've met the following two programming styles for conditions in loop bodies. This:

for (int i = 0; i < myArray.length; i++) {
    if (myArray[i].isEmpty())
        continue;

    doSomeStuff();
    doSomeMoreStuff();
}

And this:

for (int i = 0; i < myArray.length; i++) {
    if (!myArray[i].isEmpty()) {
        doSomeStuff();
        doSomeMoreStuff();
    }
}

I've usually used the first style, because it keeps indentation levels sane, especially when there's more than one condition. But I began to wonder if the second isn't actually cleaner. Do you have a preferred style and can you explain why?

Update:

Here's a more realistic example. Imagine I'm reading a file like this "First name: Last name", eg:

John;Doe
Joe;Bloggs

This is how I read each line into a name object, ignoring empty lines (which may occur):

while (line = file.readLine()) {
    if (line.isEmpty())
        continue;

    String[] columns = line.split(";");
    names.add(new Name(columns[0], columns[1]));
}

I by far prefer the second as i find it semantically easier to read. Which makes refactoring easier.

The fact is that the second increases cyclomatic complexity and may lead to the arrowhead antipattern .

In addition, keeping all your "code should not pass this point" checks at the beginning allows you to group them together, which means they are easier to maintain imho.

It depends very much on what the actual code looks like; if it's more sequential in nature, I prefer the initial continue if a condition is met, if it's branching anyway, I use a branching to skip the iteration; however if the nested levels get to deep, I might use continue anway... If you want a single style and always use that for every loop, I'd recommend the second version (branching), however, as that is less situational than continue .

It often depends on the condition. I try to avoid negated conditions, especially more complex ones. Therefore, I come out often with the first style. It is not so bad to sort out special cases at the beginning so that one can write the algorithm for the general case in one go.

I use continue (or sometimes break or goto in the middle of the loop), but always put it all on one line (so continue cannot accidentally be separated from if ), and always provide a comment of the form "if …, we're done."

Comments are a powerful tool. Ten levels of indentation really are harder to read than goto here and there. Some control-flow structures just will not document themselves.

Just refactor. The reason is that if MyObject is empty, that's an internal call it can ask itself. There is no reason to have someone else ask MyObject if it's empty just to have MyObject do something. This is logic that belongs to MyObject. You want to put the logic inside of the object as much as possible so it can be reused by other potential callers, but also so that other parts of the system don't call into things they don't have ownership of if you can avoid it.

for(MyObject object : list) {
    object.doABunchOfSimilarThings();
}

....

class MyObject {

    ...

    public void doABunchOfSimilarThings() {
        if(notEmpty()) {
            doThing1();
            doThing2();
            doThing3();        
        }
    }

    ...

}

For me it depends on the contents of the loop.

If the loop only contains up to 5 lines and a single condition, I will generally use the second style, since I find it makes it a bit easier to see when exactly the code will be executed.

If there are a lot of nested conditions (with no else ), I prefer the first syntax, for much of the same reason as you: preventing large indentation. When using the second style, each subsequent condition check needlessly increases the indentation level, reducing readability.

continue , goto , break are first words that thoughts us not to use. If method was a bit longer you will see yourself it is a bad idea. Also second lesson was use positive conditions whereever possible: )

Disclaimer: The answers in this post reflect solely on the thoughts of the answerer, and should not be taken as a general consensus .

For me, it's about readability. Even though I can understand both loop bodies, some may find time understanding them.

The 2nd loop body executes only if an element in an array isn't empty while the first finds an element of an array that is empty and tells the for loop to continue.

Readability on the 2nd loop makes it easy for conscious debugging too as if you it tells a person that the element can never be empty if doSomeStuff() or doSomeMoreStuff() is called.

The second is better. Try to avoid using continue, break, and goto ( raptors ?) while programming. They are just crutches.

The second one. If you get many indentation levels you can refactor by converting blocks to separate methods.

How about:

for (int i = 0; i < myArray.length; i++) {
    doStuffToValue(myArray[i]);    
}

void doStuffToValue(String value) {
    if (value.isEmpty()) {
        return;
    } 
    doSomeStuff();
    doSomeMoreStuff();
}

That way you make checking validity of the value a concern of the method, not of the loop body. You could even write a unit test for the doStuffToValue method if needed.

I don't think I've ever used a continue in a loop, although I use that first idiom often with return at the beginning of functions. Part of the reason style-wise is continue only works with the context of needing to skip the entire remainder of a loop, whereas the if statement works regardless of context. Therefore, if tends to be more consistent with other parts of your code.

A less subjective reason is if you're frequently skipping entire loop iterations, there's usually a way to refactor that decision to be made outside the loop, and that has performance implications. For your example, I would prefer to use a data structure that would never have empty values.

For unavoidably complex things where performance isn't an issue, I tend to prefer Adriaan's method of decomposing the loop body into functions.

Between those 3 reasons, the situations where continue is syntactically possible, is easiest to read, is consistent with other code using that data structure, and also the most performant solution just don't come up frequently enough to justify its use.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM