为什么增强的for循环比循环的正常更有效
我读到增强的for循环比这里的for循环更有效:
http://developer.android.com/guide/practices/performance.html#foreach
当我搜索它们的效率之间的差异时,我发现的是:在正常for循环的情况下,我们需要额外的步骤来找出数组的长度或大小等,
for(Integer i : list){ .... } int n = list.size(); for(int i=0; i < n; ++i){ .... }
但这是唯一的原因,增强的for循环优于正常的循环吗? 在这种情况下,更好地使用normal for循环,因为理解增强的for循环有点复杂。
检查一个有趣的问题: http : //www.coderanch.com/t/258147/java-programmer-SCJP/certification/Enhanced-Loop-Vs-Loop
任何人都可以解释这两种for循环的内部实现,或解释使用增强型for循环的其他原因吗?
说增强的for循环更有效,这有点过于简单了。 它可以 ,但在许多情况下,它几乎与旧式循环完全相同。
首先要注意的是,对于集合,增强的for循环使用Iterator
,因此如果您使用Iterator
手动迭代集合,那么您应该具有与增强的for循环相同的性能。
增强的for循环比一个天真实现的传统循环更快的地方是这样的:
LinkedList
在这种情况下,循环1将比循环2和循环3都慢,因为它将在每次迭代中(部分地)遍历列表以找到位置i
处的元素。 然而,由于使用Iterator
,循环2和3将仅在列表中进一步步骤一个元素。 循环2和3也具有几乎相同的性能,因为循环3几乎就是编译器在循环2中编写代码时将产生的结果。
我对自己今天做的一个小实验感到很惊讶。 所以我所做的是我将一定数量的元素插入链表并使用上述三种方法迭代它1)使用高级for循环2)使用Iterator 3)使用简单循环和get()
我想像你们这样的程序员可以通过查看代码更好地理解我的所作所为。
long advanced_timeElapsed,iterating_timeElapsed,simple_timeElapsed; long first=System.nanoTime(); for(Integer i: myList){ Integer b=i; } long end= System.nanoTime(); advanced_timeElapsed=end-first; System.out.println("Time for Advanced for loop:"+advanced_timeElapsed); first=System.nanoTime(); Iterator it = myList.iterator(); while(it.hasNext()) { Integer b=it.next(); } end= System.nanoTime(); iterating_timeElapsed=end-first; System.out.println("Time for Iterating Loop:"+iterating_timeElapsed); first=System.nanoTime(); int counter=0; int size= myList.size(); while(counter
结果不是我所期待的。 以下是3例中经过的时间图。
Y轴时间已过X轴测试案例
测试用例1:10输入
测试案例2:30输入
测试用例3:50输入
测试用例4:100输入
测试用例5:150输入
测试用例6:300输入
测试用例7:500输入
测试用例8:1000输入
测试案例9:2000输入
测试用例10:5000输入
测试用例11:10000输入
测试用例12:100000输入
在这里你可以看到简单的循环比其他循环更好。如果你在上面的代码中发现任何错误,请回复,我会再次检查。 在我通过字节码挖掘并查看引擎盖下发生了什么之后,我将对此进行更新。 我为这么长的回应道歉,但我喜欢描述性的。 菲利普
我读到增强的for循环比循环的正常有效。
实际上有时它对程序的效率较低,但大部分时间它完全相同。
它对开发人员来说更有效率,这通常更为重要
for-each循环在迭代集合时特别有用。
List list = for(Iterator iter = list.iterator(); list.hasNext(); ) { String s = list.next();
更容易写成(但做同样的事情,所以它对程序来说不再有效)
List list = for(String s: list) {
当通过索引访问随机访问的集合时,使用“旧”循环稍微更有效。
List list = new ArrayList (); // implements RandomAccess for(int i=0, len = list.size(); i < len; i++) // doesn't use an Iterator!
在集合上使用for-each循环总是使用Iterator,对随机访问列表的效率稍差。
AFAIK,使用for-each循环对程序来说永远不会更有效,但正如我所说,开发人员的效率往往更为重要 。
for-each使用Iterator接口。 我怀疑它比“旧”风格更有效。 迭代器还需要检查列表的大小。
这主要是为了可读性。
对于像LinkedList这样的非随机访问集合应该更快,但这种比较是不公平的。 无论如何,你不会习惯于第二次实现(使用慢速索引访问)。
foreach循环和这种循环一样有效:
for (Iterator it = list.iterator(); it.hasNext(); ) { Foo foo = it.next(); ... }
因为它完全相同。
如果使用迭代遍历列表
int size = list.size(); for (int i = 0; i < size; i++) { Foo foo = list.get(i); ... }
然后foreach循环将具有与循环相同的性能, 但仅适用于ArrayList 。 在LinkedList的情况下,你的循环将具有糟糕的性能,因为在每次迭代时,它必须遍历列表的所有节点,直到它到达第i
个元素。
foreach循环(或基于迭代器的循环,它是相同的),没有这个问题,因为迭代器保持对当前节点的引用,并在每次迭代时简单地转到下一个。 这是最好的选择,因为它适用于所有类型的列表。 它还更清楚地表达了意图,并且更安全,因为您没有冒险在循环内增加索引,或者在嵌套循环的情况下使用错误的索引。
来自Effective Java:
循环数组的标准习惯用法不一定会导致冗余检查。 现代JVM实现可以优化它们。
但Josh Bloch没有描述JVM如何优化它们。
所有答案都很好,我认为不需要更多答案,但我只想指出:
增强的for语句只能用于obtain array elements
它不能 用于 modify elements
。
如果您的程序需要修改元素,请使用传统的counter-controlled for
语句。
看一看
int[] array = { 1, 2, 3, 4, 5 }; for (int counter = 0; counter < array.length; counter++) array[counter] *= 2;
我们无法使用enhanced for
语句,因为我们正在modifying the array's elements
。