Reputation: 20179
I have a list of Objects that need to have multiple, conditionally applied, operations applied to each element. Is it more efficient to take a "if-for" approach or a "for-if" approach. To put it another way, should I minimize the number of if
statements or the number of for
loops? Is there a standard for this?
What would be a good reliable way to determine this?
"If-For" to Approach minimize if statements
public void ifForMethod() {
if (conditionA) {
for (Object o : listOfObjects) {
doA(o);
}
}
if (conditionB) {
for (Object o : listOfObjects) {
doB(o);
}
}
}
"For-If" Approach to minimize for loops
public void forIfMethod() {
for (Object o : listOfObjects) {
if (conditionA) {
doA(o);
}
if (conditionB) {
doB(o);
}
}
}
Assumptions
Upvotes: 3
Views: 420
Reputation: 3847
I think it depends on more things, like: If you find the conditionA, will the loop break? The conditionA and conditionB can coexist? Could I use if-else with them?
Just looking for what you've presented, I think the second aproach is better. You're only looping once and checking twice in the same loop. In my opinion it's also more readable.
Upvotes: 0
Reputation: 85923
First, let's take a look at the complexity of the methods that you've shown so far:
ifForMethod
performs k checks, m of which return true. For each of these m, there is an iteration over n objects. The complexity, then, is k+nm.forIfMethod
iterates over n objects and performs k comparisons on each iteration. The complexity, then, is k+n(k-1)=nk.In both cases, all k conditions have to be evaluated at least once, so the difference here really is in the nm and n(k-1) addends. Asymptotically, m is a just a fraction of k (you said m is approximately .75k), so these are both O(nk), but k+nm < k+n(k-1), so the ifForMethod
might be a faster than forIfMethod
. The difference in actual run time is going to depend on factors such as the actual time that it takes to iterate over the array, as well as the magnitude of k. You're going to start getting into issues such as memory locality (both for your objects as well as your code).
Here's an approach that you might find interesting, though. Ideally, you'd only want to iterate through the list of objects once, and you wouldn't want to have to check the boolean conditions multiple times. You could abstract away the actions that you're performing in such a way that you could combine them into a single action (and you'd only incorporate those actions that correspond to the conditions that are true), and then perform that compound action on each element in the list. Here's some code that does this.
The idea is that there are Actions, and that you can construct an Action that performs doA
and an Action that performs doB
. Based on the conditions, you can create a compound action that includes the doA
action if the doA
condition is true, and the doB
action if the doB
condition is true. Then you iterate through the objects, and call perform the compound action on each object. Asymptotically, this is a k+nm method, so in theory it performs nicely, but again, the actual performance here will depend on some of those tricky constants, and memory locality issues.
import java.util.ArrayList;
import java.util.List;
public class CompoundActionExample {
/**
* An action is used to do something to an argument.
*/
interface Action {
void act( Object argument );
}
/**
* A compound action is an action that acts on an argument
* by passing the argument to some other actions.
*/
static class CompoundAction implements Action {
/**
* The list of actions that the compound action will perform. Additional
* actions can be added using {@link #add(Action)}, and this list is only
* accessed through the {@link #act(Object)} method.
*/
private final List<CompoundActionExample.Action> actions;
/**
* Create a compound action with the specified list of actions.
*/
CompoundAction( final List<CompoundActionExample.Action> actions ) {
this.actions = actions;
}
/**
* Create a compound action with a fresh list of actions.
*/
CompoundAction() {
this( new ArrayList<CompoundActionExample.Action>() );
}
/**
* Add an action to the compound action.
*/
public void add( CompoundActionExample.Action action ) {
actions.add( action );
}
/**
* Act on an argument by passing the argument to each of the
* compound action's actions.
*/
public void act( final Object argument) {
for ( CompoundActionExample.Action action : actions ) {
action.act( argument );
}
}
}
public static void main(String[] args) {
// Some conditions and a list of objects
final boolean conditionA = true;
final boolean conditionB = false;
final Object[] listOfObjects = { "object1", "object2", "object3" };
// A compound action that encapsulates all the things you want to do
final CompoundAction compoundAction = new CompoundAction();
// If conditionA is true, add an action to the compound action that
// will perform doA. conditionA is evaluated exactly once.
if ( conditionA ) {
compoundAction.add( new Action() {
public void act( final Object argument) {
System.out.println( "doA("+argument+")" ); // doA( argument );
}
});
}
// If conditionB is true, add an action to the compound action that
// will perform doB. conditionB is evaluted exactly once.
if ( conditionB ) {
compoundAction.add( new Action() {
public void act(Object argument) {
System.out.println( "doB("+argument+")" ); // doB( argument );
}
});
}
// For each object, apply the compound action
for ( final Object o : listOfObjects ) {
compoundAction.act( o );
}
}
}
Upvotes: 1
Reputation: 64933
Using what you called the "If-For" way rather than "For-If" is (perhaps a slightly more general version of) an optimization called loop unswitching. Whether it's actually a good idea depends on several factors, such as (but not limited to)
doA
and doB
may be reordered)Upvotes: 2
Reputation: 1838
There is no reason to make 2 passes over the list.
Assumptions: predicates are simple booleans, if they have to be evaluated then obviously the cost can change things.
If ((condtionA || conditionB) == true) then both If-for and For-If are both 1 pass. If both predicates can be true then obviously you only want to make one pass.
It doesn't matter what doA and doB since we're assuming they're they same in both If-for and For-If.
If the predicates can change over the course of evaluation then that must be considered.
You're asking a general question so answers are general and vague without more details.
Ok now that you've provided additional info (the list is only 5 elements long, this is part of a build process, the predicates are static booleans) we can see that the bottleneck here is the doA/B functions. Therefore you should only go through the loop once. The static boolean checks are negligible.
Upvotes: 4
Reputation: 7247
The second one is more efficent in terms of how many comparisons you make.
Check condition a, 1 calculation. If true, Object.size calculatons.
Check condition b, 1 calculation. If true, Object.size calculations. Min, 2, Max Object.size * 2
For Method 2, you will always have Object.size * 2 calculations performed.
Consider your "worst case" if both checks are false. Way 1 will only do 2 calculations. Way 2 will perform Object.size * 2. It has nothing to do with your function as in both cases it will always take the same amount of time in both cases.
Even in your "best case" if both checks are true, you are still performing that check N-1 times more for A, and N-1 times more for B.
Best way I think to do it with the fewest calculations.
public void ifForMethod() {
if (conditionA) {
if(conditionB){
for (Object o : listOfObjects) {
doA(o);
doB(o);
}
else{
for (Object o : listOfObjects) {
doA(o);
}
}
}
else if (conditionB) {
for (Object o : listOfObjects) {
doB(o);
}
}
}
You perform 2 check operations and then only loop through the list once, at max.
Upvotes: 0
Reputation: 20614
It depends! The ifForMethod()
solution is best, if there are real cases where neither conditionA nor conditionB is true. If there are cases where conditionA and conditionB are true, solution forIfMethod()
is the best but the conditions should be precalculated before entering the loop.
But you can modify forIfMethod()
to make it suitable for all cases:
public void forIfMethod() {
boolean a = conditionA;
boolean b = conditionB;
if (a || b) {
for (Object o : listOfObjects) {
if (a) {
doA(o);
}
if (b) {
doB(o);
}
}
}
}
Upvotes: 1
Reputation: 1520
lets consider that we are doing same number of operation in both the for loop and inside if .With this standard i will go with the first approach which using if statement before executing for loop just to avoid the number of iteration in for loop.
Also as you are using advance for loop which takes more time to execute the same operation compare to normal for loop.
please correct me if i am wrong.
Upvotes: 1
Reputation: 562
It Depends on the nature of the business problem your code is trying to solve. If both conditionA AND conditionB are simple Boolean variables but not expressions, then the For-If is going to be better as you are cycling through the list of objects only once.
We are basically comparing which performs better : Enumerating from a list of objects multiple times or evaluating a boolean expression multiple times. If your conditionA/conditionB are very complex Boolean expressions, then your If-For would be a better approach.
Upvotes: 1
Reputation: 31658
Several things:
Upvotes: -1
Reputation: 54742
the first one (if-for) sounds good for me.. because for first case there will be a single checking for whole for loop. But in the second cases there will be checking for every loop.
Upvotes: 0