Java 编译器如何处理for循环中的size方法
假设Java 编译器如何处理for循环中的size方法,java,arraylist,Java,Arraylist,假设list是ArrayList,下面两个代码段中哪一个更快 for(int i=0; i<list.size();i++){...} 澄清 在for循环中,编译器每次调用list.size(),还是调用一次并随后使用它 注意对list.size()的每次调用实际上都会对项目进行计数。这就是问题的本质。任何一个好的编译器都会将这些编译成完全相同的汇编代码。int count=list.size(); int count = list.size(); for(int i=0; i<c
list
是ArrayList,下面两个代码段中哪一个更快
for(int i=0; i<list.size();i++){...}
澄清
在for循环中,编译器每次调用list.size()
,还是调用一次并随后使用它
注意对
list.size()
的每次调用实际上都会对项目进行计数。这就是问题的本质。任何一个好的编译器都会将这些编译成完全相同的汇编代码。int count=list.size();
int count = list.size();
for(int i=0; i<count;i++){...}// Is faster
对于(int i=0;i你应该问自己一些问题:
- 我的表现有问题吗
- 我是否分析了此代码的瓶颈
如果booth的问题可以用“是”来回答,那么尝试两种方法并描述结果
事实上,我认为这不会有什么区别。在大多数情况下,速度是无法区分的。也就是说,如果在循环执行时集合可能正在更改,则您的两个循环在语义上是不同的。对集合的修改可能在循环中(或在循环中调用的代码中)或者在并发执行的不同线程中
具体回答“澄清”:是的,每次通过循环都会调用size
方法
除非我有明确的证据表明优化很重要,否则我永远不会编写这个循环的“优化”版本(集合也不会改变)。如果你的代码经过了如此多的调整,这样的调整会给你带来可测量的加速,你应该会非常高兴。ArrayList list=new ArrayList();
ArrayList list = new ArrayList();
for(int i=0; i<1000000;i++){
list.add("Object added "+i);
}
long startTime =System.currentTimeMillis();
for(int i=0; i<list.size();i++){
System.out.println(list.get(i));
}
long endTime = System.currentTimeMillis();
int count=list.size();
long startTime1 =System.currentTimeMillis();
for(int i=0; i<count;i++){
System.out.println(list.get(i));
}
long endTime1 = System.currentTimeMillis();
System.out.println("Exe1 time in millis secs"+(endTime-startTime));
System.out.println("Exe2 time in millis secs"+(endTime1-startTime1));
OutPut
...........
.............
Object added 999999
Exe1 time in millis secs14131
Exe2 time in millis secs14106
对于(int i=0;i我认为第二段代码稍微快一些。方法调用可能需要将代码加载到内存中,并将数据从方法内存堆栈加载到方法内存堆栈(如第一段代码)。这显然比单个内存访问(第一段代码)慢但是,如果循环迭代期间数组大小可能发生变化,我认为使用第一段代码更安全。请考虑下面的例子:
for(int i=0; i<list.size();i++){
if list.get(i) == 0 { list.remove(i); i--; }
}
用于(int i=0;我在这里谈论的是纳秒……这真的有那么重要吗?我认为可读性应该是你更关心的问题。这个迭代需要纳秒。但是如果它被用于一个非常繁重的算法呢。所有处理繁重算法的有效代码片段都依赖于这些问题。分析两种解决方案,以数字和数字形式获得结果为您的特定平台找到答案。不过,这是一个微观优化,它对占用您的真实世界时间没有任何好处。当您进行优化时,您还可以在循环外初始化i
,ala C.可能会为您节省几皮秒。不过,说真的,它们应该编译成相同的东西。请注意,每次调用list.size()将实际去计算项目。这就是问题的本质。既然您说您使用ArrayList
作为列表
实现,那么您的陈述是错误的。请参阅我的澄清部分否,因为a)java编译器几乎没有进行优化,因为这将降低JIT的好处;b)它将使行为人最终成为关注必须如何解决真正的性能问题的人。这是因为它是(或者至少是:可能是)多线程使用错误。@ChristianKuetbach这不是多线程的问题,列表的大小在任何情况下都是错误的。但是如果列表没有改变,这是一个可行的优化,尽管我会声明count为final
。我认为在大多数情况下,在没有迭代器()的情况下进行迭代时更改列表将导致ConcurrenModificationException:)@ChristianKuetbach我认为,当(!list.isEmpty()){list.remove(0);}
在循环构造中调用size
时,会更改基础数据结构,并且不会引发此异常;-)ConcurrenModificationException将在尝试同时访问迭代器和列表时发生。例如,在循环到迭代器并从列表或映射中删除元素时。在某些情况下,“优化”版本也可能是错误的(以此列表的多线程使用为例)。同意。因此“…您的两个循环在语义上不同…”您使用的是“System.out.println()”的时间,而不是循环。与list.size()的更改相比,使用System.out的速度非常慢。这就是我所说的“profile”。使用profile4j或eclipse profiler并对我的注释进行版本化。鉴于System.out.println的速度慢得惊人,它的速度提升了0.1%,实际上看起来相当不错(假设它是可复制的)。说真的,我的整个3D游戏因为一些打印声明而结结巴巴地停止了
ArrayList list = new ArrayList();
for(int i=0; i<1000000;i++){
list.add("Object added "+i);
}
long startTime =System.currentTimeMillis();
for(int i=0; i<list.size();i++){
System.out.println(list.get(i));
}
long endTime = System.currentTimeMillis();
int count=list.size();
long startTime1 =System.currentTimeMillis();
for(int i=0; i<count;i++){
System.out.println(list.get(i));
}
long endTime1 = System.currentTimeMillis();
System.out.println("Exe1 time in millis secs"+(endTime-startTime));
System.out.println("Exe2 time in millis secs"+(endTime1-startTime1));
OutPut
...........
.............
Object added 999999
Exe1 time in millis secs14131
Exe2 time in millis secs14106
for(int i=0; i<list.size();i++){
if list.get(i) == 0 { list.remove(i); i--; }
}