Java for循环是否在每次迭代中重新计算其体中的函数?

Java for循环是否在每次迭代中重新计算其体中的函数?,java,c++,c,for-loop,Java,C++,C,For Loop,当我写这样一个简单的for循环时 for (int i = 0; i < myList.size(); i++){} for(inti=0;i不存在标准中优化函数调用的要求(例如只调用一次)。p> 编译器的优化引擎需要知道size的返回类型以及值是否会更改。困难的事业 如果希望函数调用一次,请在循环之前将结果指定给常量变量: const size_t size = myList.size(); for (size_t i = 0; i < size; ++i) { // ...

当我写这样一个简单的for循环时

for (int i = 0; i < myList.size(); i++){}
for(inti=0;i
在爪哇,C和C++中,它在每次迭代中重新评估MyList.siz(),还是在循环的开始时只评估一次?如果重新评估,是否预先评估大小,即

int n = myList.size();
for (int i = 0; i < n; i++){}
int n=myList.size();
对于(int i=0;i
在性能方面给了我们一些重要的东西

对于Java:

for (int i=0; i<myList.size(); i++){}
无论如何(如果可能的话)

您可以在Java语言规范的第章中阅读相关内容。

从逻辑上讲,在每次迭代中都会对条件进行评估

通过查看示例可以看出这一点,因为表达式中的
i
值每次都会更改


实际上,它可能取决于语言或实现。编译器或运行时可以缓存或优化表达式的某些部分。从孤立地看代码,你不一定能说出。

< P>在C和C++中,控制表达式在循环的每个迭代中被评估。 根据本规范第6.8.5.3节:

声明

for (clause-1; expression-2; expression-3) statement
行为如下:表达式
expression-2
是 每次执行前计算的控制表达式 循环体的。
表达式
expression-3
的计算公式为 循环体每次执行后的空表达式。如果
第1条
是一种声明,它声明了任何标识符的范围 是声明和整个循环的其余部分,包括 其他两种表达方式;它是按照之前的执行顺序到达的 控制表达式的第一次求值。如果
条款-1
是 表达式,则在第一个 控制表达的评估

本协议第6.5.3节包含类似的措辞


因此,如果控制表达式涉及一个函数调用或其他一些在循环体中不会更改的潜在昂贵计算,则应事先对其求值,将其存储在变量中,然后在控制表达式中使用该变量。

取决于是否在for循环中修改myList。此外,编译器(尤其是C语言中的编译器)试图通过将求值移出循环来优化这些代码。优化也基于对象是否在循环内修改。在C和C++中,

< P>不存在标准中优化函数调用的要求(例如只调用一次)。p> 编译器的优化引擎需要知道
size
的返回类型以及值是否会更改。困难的事业

如果希望函数调用一次,请在循环之前将结果指定给常量变量:

const size_t size = myList.size();
for (size_t i = 0; i < size; ++i)
{
  // ...
}
const size\u t size=myList.size();
对于(大小i=0;i

如果在循环中更改了
myList
,则所有保证均为off。每次迭代都需要调用函数
size

我只熟悉C/C++,正确的答案是:有时会计算,有时不会。编译器保证它将在每次迭代中计算它认为与原始代码等价的汇编代码(但不是您真正想要的)。如果在调试模式下编译代码(没有编译器优化),或者变量的定义中包含关键字volatile,或者更改列表的大小,那么每次迭代都会计算条件(列表的大小)。但是,如果您使用速度优化进行编译,不要在循环中更改列表,那么现代编译器足够聪明,可以将列表的大小存储在寄存器中,而不是每次迭代都对其求值。此外,函数list.size()将是内联的,不会实际调用代价高昂的函数调用

请注意,在多线程编程中,这是灾难性的。一个线程可以将元素附加到列表中,而另一个在列表上迭代的线程将永远看不到新元素。要强制评估,列表的大小必须定义为volatile(或者,如果熟悉汇编,可以使用内存屏障)。无论如何,当您编译应用程序进行生产时,请确保启用编译器优化,并且每次迭代中所谓的函数调用的运行时惩罚将被取消

另一个问题:一些非常激进的编译器优化甚至可能展开循环。例如,如果列表的大小为403个元素,编译器可能会执行100次迭代,每次循环中的代码重复4次,再加上使用原始代码对另一个循环进行3次迭代。所以总共有103次迭代,而不是403次(所以不可能争论代码是否在每次迭代中执行,因为很难定义什么是迭代)。 此类激进优化的示例:复制长字符串或内存缓冲区。如果您复制X字节,在64位机器上,执行X/8迭代(一次复制64位)+在单独的循环中执行剩余的0..7字节实际上更快。有些处理器甚至在一次操作中支持128字节和最多512字节的单位

最后一条建议:编写一个易于理解的代码非常重要,因此在本例中,我会在上面的答案中使用好的建议(关于java列表迭代器),或者故意将list.size()保存到时态变量中

list中的常量\u size=list.size()


只是为了便于阅读/调试

哪种语言?C++、C或java?选择一种语言。您提到的每一个都有一个不同的规范文档来控制它的行为。这里没有C/C++这样的东西。不要散布这种胡说八道。@StoryTeller:它现在是一个更大的超集,但仍然是一个超集。@DaveDoknjas-它不是,而且是
const size_t size = myList.size();
for (size_t i = 0; i < size; ++i)
{
  // ...
}