我有一个对象列表,需要对每个元素应用多个有条件应用的操作。采用“if-for”方法或“for-if”方法更有效率。换句话说,我应该最小化if
语句的数量还是for
循环的数量?这有标准吗?
确定这个的可靠方法是什么?
“If-For” 接近最小化if语句
public void ifForMethod() {
if (conditionA) {
for (Object o : listOfObjects) {
doA(o);
}
}
if (conditionB) {
for (Object o : listOfObjects) {
doB(o);
}
}
}
“For-If” 最小化for循环的方法
public void forIfMethod() {
for (Object o : listOfObjects) {
if (conditionA) {
doA(o);
}
if (conditionB) {
doB(o);
}
}
}
的假设 的
答案 0 :(得分:4)
没有理由在列表上进行2次传递。
假设:谓词是简单的布尔值,如果必须进行评估,那么显然成本可能会改变。
如果((condtionA || conditionB)== true)则If-for和For-If都是1遍。如果两个谓词都可以为真,那么显然你只想进行一次传递。
doA和doB并不重要,因为我们假设它们在If-for和For-If中都是相同的。
如果谓词可以在评估过程中发生变化,则必须考虑这一点。
你问的是一般问题,所以答案是一般性的,含糊不清,没有更多细节。
好了,现在你已经提供了额外的信息(列表只有5个元素长,这是构建过程的一部分,谓词是静态布尔值)我们可以看到这里的瓶颈是doA / B函数。因此,您应该只进行一次循环。静态布尔检查可以忽略不计。
答案 1 :(得分:2)
使用您所谓的“If-For”方式而非“For-If”方式(可能是更为通用的版本)称为loop unswitching的优化。它是否真的是一个好主意取决于几个因素,例如(但不限于)
doA
和doB
可以重新排序)答案 2 :(得分:1)
它取决于您的代码试图解决的业务问题的性质。如果conditionA和conditionB都是简单的布尔变量而不是表达式,那么For-If会更好,因为您只在对象列表中循环一次。
我们基本上比较哪个表现更好:多次枚举对象列表或多次评估布尔表达式。如果你的conditionA / conditionB是非常复杂的布尔表达式,那么你的If-For将是一个更好的方法。
答案 3 :(得分:1)
让我们考虑我们在for循环和内部都进行相同数量的操作。如果这个标准我将采用第一种方法,在执行for循环之前使用if语句只是为了避免for循环中的迭代次数
此外,当您使用advance for循环时,与正常循环相比,执行相同操作需要更多时间。
如果我错了,请纠正我。答案 4 :(得分:1)
这取决于!如果存在条件A和条件B都不为真的实际情况,则ifForMethod()
解决方案是最佳的。如果存在conditionA和conditionB为真的情况,则解forIfMethod()
是最好的,但在进入循环之前应该预先计算条件。
但您可以修改forIfMethod()
以使其适合所有情况:
public void forIfMethod() {
boolean a = conditionA;
boolean b = conditionB;
if (a || b) {
for (Object o : listOfObjects) {
if (a) {
doA(o);
}
if (b) {
doB(o);
}
}
}
}
答案 5 :(得分:1)
首先,让我们来看看你到目前为止所展示的方法的复杂性:
ifForMethod
执行 k 检查, m 返回true。对于每个 m ,都会对 n 对象进行迭代。那么,复杂性是 k + nm 。forIfMethod
迭代 n 对象,并在每次迭代时执行 k 比较。那么,复杂性是 k + n(k-1)= nk 。在这两种情况下,所有 k 条件必须至少评估一次,因此这里的区别实际上是 nm 和 n(k-1) )加数。渐近地, m 只是 k 的一小部分(你说 m 大约是 .75k ),所以这些都是O( nk ),但是 k + nm< k + n(k-1),因此ifForMethod
可能比forIfMethod
更快。实际运行时间的差异取决于诸如迭代数组所花费的实际时间以及 k 的大小等因素。您将开始涉及内存局部性问题(包括对象和代码)。
不过,这是一种你可能会感兴趣的方法。理想情况下,您只需要遍历对象列表一次,并且您不希望多次检查布尔条件。您可以抽象出您正在执行的操作,以便将它们组合成单个操作(并且您只需合并那些与真实条件相对应的操作),然后执行该复合操作列表中的每个元素。这是执行此操作的一些代码。
这个想法是有动作,你可以构建一个执行doA
的动作和一个执行doB
的动作。根据条件,您可以创建一个复合操作,如果doA
条件为true,则包含doA
操作;如果doB
条件为true,则可以创建doB
操作。然后迭代遍历对象,并调用对每个对象执行复合操作。渐近地,这是一个 k + nm 方法,所以理论上它表现很好,但同样,这里的实际性能将取决于一些棘手的常量和内存局部性问题。
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 );
}
}
}
答案 6 :(得分:0)
第一个(if-for)对我来说听起来不错..因为对于第一种情况,将会有一次检查整个for循环。但在第二种情况下,将检查每个循环。
答案 7 :(得分:0)
我认为这取决于更多的事情,例如:如果你发现conditionA,循环会破坏吗? conditionA和conditionB可以共存吗?我可以使用if-else吗?
只是在寻找你所呈现的内容,我认为第二种方法更好。您只需循环一次并在同一循环中检查两次。在我看来,它也更具可读性。
答案 8 :(得分:0)
第二个就你做了多少比较而言更有效率。
检查条件a,1计算。 如果为true,则为Object.size计算器。
检查条件b,1计算。 如果为true,则为Object.size计算。 最小值,2,Max Object.size * 2
对于方法2,您将始终执行Object.size * 2计算。
如果两项检查均为假,请考虑您的“最坏情况”。方式1只进行2次计算。方式2将执行Object.size * 2.它与您的函数无关,因为在这两种情况下它总是需要相同的时间。
即使在你的“最佳情况”中,如果两项检查都是真的,你仍然会对A进行N-1次检查,对B进行N-1次检查。
我认为最好的办法是用最少的计算来做。
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);
}
}
}
您执行2次检查操作,然后仅在最大值
循环列表一次答案 9 :(得分:-1)
有几件事: