我目前正在审核包含此内容的PullRequest:
- for (int i = 0; i < outgoingMassages.size(); i++) {
+ for (int i = 0, size = outgoingMassages.size(); i < size; i++)
https://github.com/criticalmaps/criticalmaps-android/pull/52
不知怎的,我感觉不对 - 会认为虚拟机正在进行这些优化 - 但不能确切地说。如果这个改变有意义,或者确认这是在VM端完成的,那么我们会喜欢获得一些输入。答案 0 :(得分:5)
不,不确定VM是否会改变您的代码
- for (int i = 0; i < outgoingMassages.size(); i++) {
到
+ for (int i = 0, size = outgoingMassages.size(); i < size; i++)
在for循环中,outgoingMassages
可能会改变其大小。因此,这种优化不能由JVM应用。如果这是共享资源,另一个线程也可以更改outgoingMassages
大小。
只有在行为没有改变的情况下,JVM才能更改代码。例如,它可以将一个字符串连接列表替换为StringBuilder
的附加序列,或者它可以内联一个简单的方法调用,或者如果它是一个常量值,则可以计算一个循环外的值。
答案 1 :(得分:1)
VM不会执行此优化。由于size() - Method可能不会在每次调用时返回相同的结果。因此必须在每次迭代时调用该方法。
但是,如果尺寸是一种简单的吸气方法,则性能影响非常小。可能无法衡量。 (在少数情况下,它可能使Java能够使用并行化,这可能会产生影响,但这取决于循环的内容)。
更大的差异可能是确保for循环具有预先已知的迭代量。在这个例子中,我似乎没有意义。但也许被调用的方法可能会返回不需要的变化结果?
答案 2 :(得分:0)
如果集合中的size()
方法只是提供私有字段的值,那么VM将优化其中的大部分(但不是全部)。它通过内联size()
方法来实现,这样它就可以访问该字段。
未获得优化的剩余位是新代码中的size
将被视为final
,因此会保持不变,而从该集合中获取的字段将赢得&#39 ;被视为final
(也许它是从另一个线程修改过来的)。因此,在原始情况下,每次迭代都会读取字段,但在新的情况下,它会赢得。
答案 3 :(得分:0)
任何体面的优化器 - 无论是在VM还是在编译器中 - 都可能会识别出来:
class Messages {
int size;
public int size() {
return size;
}
}
public void test() {
Messages outgoingMassages = new Messages();
for (int i = 0; i < outgoingMassages.size(); i++) {
}
}
并将其优化为
for (int i = 0; i < outgoingMassages.size; i++) {
执行额外的 - 未经测试 - 因此应优先考虑evil。
答案 4 :(得分:0)
方法调用将在循环的每次迭代时发生,并且不是免费的成本。由于您无法预测这种情况发生的频率,因此将其称为一次将始终 。这是一个次要的优化,但您不应该依赖编译器为您进行优化。
此外,会员outgoingMassages
..
private ArrayList<OutgoingChatMessage> outgoingMassages ..
...应该是一个界面:
private List<OutgoingChatMessage> outgoingMassages ..
然后,调用.size()
将成为虚拟方法。要找出特定的对象类,将为层次结构的所有类调用方法表。这不是免费的。