List<String> flowers = new ArrayList<String>();
我的for循环目前看起来像这样...
for (int i = 0; i < flowers.size(); i++) {
...
}
我应该将这个代码更改为下面给出的代码吗?
int size = flowers.size();
for (int i = 0; i < size; i++) {
...
}
如果我有一个大的花卉数组,哪种方式更高效?我猜应该是后者。
List<String> flowers = new ArrayList<String>();
我的for循环目前看起来像这样...
for (int i = 0; i < flowers.size(); i++) {
...
}
我应该将这个代码更改为下面给出的代码吗?
int size = flowers.size();
for (int i = 0; i < size; i++) {
...
}
如果我有一个大的花卉数组,哪种方式更高效?我猜应该是后者。
最好使用for-each循环 [更易读]
for (Flower flower :flowers){
//...
}
我使用javap
命令对以下代码进行了指令转储:
public void forLoop1() {
List<String> lst = new ArrayList<String>();
for (int i = 0; i < lst.size(); i++) {
System.out.println("hi");
}
}
public void forLoop2() {
List<String> lst = new ArrayList<String>();
int size = lst.size();
for (int i = 0; i < size; i++) {
System.out.println("hi");
}
}
public void forLoop1();
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: iload_2
11: aload_1
12: invokeinterface #4, 1; //InterfaceMethod java/util/List.size:()I
17: if_icmpge 34
20: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
23: ldc #6; //String hi
25: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
28: iinc 2, 1
31: goto 10
34: return
public void forLoop2();
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: invokeinterface #4, 1; //InterfaceMethod java/util/List.size:()I
14: istore_2
15: iconst_0
16: istore_3
17: iload_3
18: iload_2
19: if_icmpge 36
22: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
25: ldc #6; //String hi
27: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
30: iinc 3, 1
33: goto 17
36: return
它对我没有进行优化。
java版本号为"1.6.0_22”,Java(TM) SE Runtime Environment(构建1.6.0_22-b04)Java HotSpot(TM) Client VM(构建17.1-b03,混合模式,共享)
因此,如果您需要从提到的两个中选择一个,请选择第二个,但我个人会选择for-each
。
摘自 Joshua Bloch 的Effective Java第46条:
在1.5版中引入的for-each循环通过隐藏迭代器或索引变量完全消除了混乱和错误的机会。由此产生的习语同样适用于集合和数组:
当您看到冒号(:)时,将其解读为“在”的意思。因此,上面的循环可以理解为“对于每个元素e在elements中”。请注意,即使对于数组,使用for-each循环也不会带来性能损失。实际上,在某些情况下,它可能比普通的for循环提供轻微的性能优势,因为它仅计算一次数组索引的限制。虽然程序员可以手动完成这一点(Item 45),但并非总是这样做。// The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); }
另请参阅
很抱歉要说,但 @Jigar 的答案是错误的。这才是正确的答案。(简而言之,不要使用 for : each
)。
import java.util.ArrayList;
import java.util.List;
public class LoopTest {
public static void main(String s[]) {
long start, end;
List<Integer> a = new ArrayList<Integer>();
for (int i = 0; i < 2500000; i++) {
a.add(i);
}
///// TESTING FOR : EACH LOOP
start = System.currentTimeMillis();
for (Integer j : a) {
int x = j + 3;
}
end = System.currentTimeMillis();
System.out.println(end - start
+ " milli seconds for [ Integer j : a ] ");
////// TESTING DEFAULT LOOP
start = System.currentTimeMillis();
for (int i = 0; i < a.size(); i++) {
int x = a.get(i) + 3;
}
end = System.currentTimeMillis();
System.out.println(end - start
+ " milli seconds for [ int i = 0; i < a.length; i++ ] ");
////// TESTING SLIGHTLY OPTIMIZED LOOP
start = System.currentTimeMillis();
int size = a.size();
for (int i = 0; i < size; i++) {
int x = a.get(i) + 3;
}
end = System.currentTimeMillis();
System.out.println(end - start
+ " milli seconds for [ int i = 0; i < size; i++ ] ");
//// TESTING MORE OPTIMIZED LOOP
start = System.currentTimeMillis();
for (int i = size; --i >= 0;) {
int x = a.get(i) + 3;
}
end = System.currentTimeMillis();
System.out.println(end - start
+ " milli seconds for [ int i = size; --i >= 0; ] ");
}
}
结果:
96 milli seconds for [ Integer j : a ]
57 milli seconds for [ int i = 0; i < a.length; i++ ]
31 milli seconds for [ int i = 0; i < size; i++ ]
31 milli seconds for [ int i = size; --i >= 0; ]
你可以自行判断,但是将太多功劳归于JVM优化器是不合适的。你仍然需要在自己的代码中做出明智的选择,并且几乎从来不建议使用for:each
语法。正如你所看到的,将size放入它自己的变量中是个好主意。
尽管这些优化可能依赖于JVM(其中一些可能会随着JIT而启动),但重要的是要知道Java做了什么,以及Java没做什么。
for : each
始终速度较慢,而频繁调用 .size()
比分配到一个变量中要慢一些。 - Ravi ParekhJVM 无法对其进行优化,因为 size()
是一个方法,JVM 在这种情况下无法(也不会尝试)确定 size()
始终会返回相同的值。只要 size()
的值不变,第二个方案略微更有效率,但效益非常小,你甚至不必考虑使用它。
如果在迭代期间更改了数组列表,则行为会不同。但我猜你不这样做。根据我的测试,后者通常更快(尤其是在像安卓这样的系统上)。我会这样写:
for (int i = 0, size = flowers.size(); i < size; i++) {
...
}
根据Java语言规范(14.14.1):
基本的for语句执行一些初始化代码,然后执行一个表达式,一个语句和一些更新代码重复,直到表达式的值为false。
在您的第一个示例中,表达式是i < flowers.size()
,并且在每次迭代中都会被评估一次。在您的特殊情况下,这不应该产生显着差异,因为ArrayList
上的flowers.getSize()
是一个非常短的方法。但是,通常情况下,如果表达式的结果对于每次迭代都相同且昂贵,则进行预计算。
结果:这必须在Java虚拟机的每个实现中产生相同的输出,并证明表达式在每次迭代中只评估一次:
int counter2 = 10;
for (int counter1 = 0; counter1 < counter2; counter1++) {
System.out.println(counter1 + ", " + counter2);
counter2--;
}
输出:
0, 10
1, 9
2, 8
3, 7
4, 6
[ int i = 0; i < size; i++ ]
1.6
-server
7.968242071 milli seconds for [ Integer j : a ]
7.206275775999999 milli seconds for [ int i = 0; i < a.length; i++ ]
1.5864E-5 milli seconds for [ int i = 0; i < size; i++ ]
14.774186076999998 milli seconds for [ int i = size; --i >= 0; ]
-client
83.36101683999999 milli seconds for [ Integer j : a ]
44.288568631 milli seconds for [ int i = 0; i < a.length; i++ ]
2.3191E-5 milli seconds for [ int i = 0; i < size; i++ ]
24.826621246 milli seconds for [ int i = size; --i >= 0; ]
1.7
-server
7.029150422 milli seconds for [ Integer j : a ]
6.6269827779999995 milli seconds for [ int i = 0; i < a.length; i++ ]
1.3852E-5 milli seconds for [ int i = 0; i < size; i++ ]
13.842110377 milli seconds for [ int i = size; --i >= 0; ]
13.868426141 milli seconds for [ int i = a.size()-1; i >= 0; i-- ]
1.6618000000000003E-5 milli seconds for [ int i = 0; i < a.size(); i++ ]
-client
7.382479727 milli seconds for [ Integer j : a ]
6.748068759 milli seconds for [ int i = 0; i < a.length; i++ ]
1.4162999999999998E-5 milli seconds for [ int i = 0; i < size; i++ ]
13.951547335999999 milli seconds for [ int i = size; --i >= 0; ]
13.929234053999998 milli seconds for [ int i = a.size()-1; i >= 0; i-- ]
1.6873E-5 milli seconds for [ int i = 0; i < a.size(); i++ ]
测试代码:
public static void main(String s[]) {
long start=0, end = 0, delta = 0;
//int[] a = new int[2500000];
List<Integer> a = new ArrayList<Integer>();
int x = 0;
for (int i = 0; i < 2500000; i++) {
a.add(i);
}
start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
start = System.nanoTime();
for (Integer j : a) {
x = j + 3;
}
end = System.nanoTime();
delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ Integer j : a ] ");
start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
start = System.nanoTime();
for (int i = 0; i < a.size(); i++) {
x = a.get(i) + 3;
}
end = System.nanoTime();
delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < a.length; i++ ] ");
int size = a.size();
start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
x = a.get(i) + 3;
}
end = System.currentTimeMillis();
delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < size; i++ ] ");
start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
start = System.nanoTime();
for (int i = size; --i >= 0;) {
x = a.get(i) + 3;
}
end = System.nanoTime();
delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = size; --i >= 0; ] ");
start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
start = System.nanoTime();
for (int i = a.size()-1; i >= 0; i--) {
x = a.get(i) + 3;
}
end = System.nanoTime();
delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = a.size()-1; i >= 0; i-- ] ");
start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
start = System.currentTimeMillis();
for (int i = 0; i < a.size(); i++) {
x = a.get(i) + 3;
}
end = System.currentTimeMillis();
delta += end - start;
}
System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < a.size(); i++ ] ");
System.out.println(x);
}
另外,如果你想知道使用方法调用作为源集合是否会有任何性能影响。也就是说 - 方法会被调用多次吗?答案是否定的。以下是一个例子:
import java.util.*;
public class TestForeach {
public static void main (String[] args) {
for (String s : getStrings()) {
System.out.println("The string was: "+s);
}
}
private static List<String> getStrings() {
System.out.println("IN GET STRINGS");
return Arrays.asList("A","B","C");
}
}
IN GET STRINGS
The string was: A
The string was: B
The string was: C
for (int i = 0; i < list.size(); i++)
和微优化的for循环for (int i = -1, size = list.size(); ++i < size;)
。我在eclipse和命令行中都运行了测试,并发现了巨大的差异。Time for Original: 32552 ms Time for MicroOptimized 32707 ms
Fastest Loop: Original
Slowest loop takes 0.47616121897272057% more time
从命令行运行的结果:
Time for Original: 274489 ms Time for MicroOptimized 30516 ms
Fastest Loop: MicroOptimized
Slowest loop takes 799.4920697339101% more time
public static void main(String[] args) {
List<Byte> list = initializeList();
byte value = 0;
final int NUM_LOOPS = 100;
long startOriginal, startOptimized, endOriginal, endOptimized;
startOptimized = System.currentTimeMillis();
for (int j = 0; j < NUM_LOOPS; j++) {
for (int i = -1, size = list.size(); ++i < size;) {
value = list.get(i);
}
}
endOptimized = System.currentTimeMillis();
startOriginal = System.currentTimeMillis();
for (int j = 0; j < NUM_LOOPS; j++) {
for (int i = 0; i < list.size(); i++) {
value = list.get(i);
}
}
endOriginal = System.currentTimeMillis();
System.out.println(value);
printResults(startOriginal, endOriginal, startOptimized, endOptimized);
}
private static void printResults(long startOriginal, long endOriginal,
long startOptimized, long endOptimized) {
long timeOriginal = endOriginal - startOriginal;
long timeOptimized = endOptimized - startOptimized;
long diff = Math.abs(timeOriginal - timeOptimized);
long min = Math.min(timeOriginal, timeOptimized);
System.out.println("Time for Original: " + timeOriginal + " ms"
+ " Time for MicroOptimized " + timeOptimized + " ms");
System.out.println("Fastest Loop: "
+ ((timeOriginal < timeOptimized) ? "Original"
: "MicroOptimized"));
System.out.println("Slowest loop takes " + ((double) 100 * diff / min)
+ "% more time");
}
public static List<Byte> initializeList(){
List<Byte> list = new ArrayList<Byte>();
final Byte ONE = new Byte((byte) 1);
for (int i = 0; i < Integer.MAX_VALUE / 10; i++) {
list.add(ONE);
}
return list;
}
}
两种方法都可以。根据JVM的不同,第二种方法可能会快几个时钟周期,但这只是微不足道的差异。要注意这些类型的次优化。除非您正在构建实时系统,在那里每个CPU时钟都很重要,否则它们只会增加复杂性和错误来源。
我建议使用迭代器结构(如已经建议的那样)。
for (Flower flower: flowers) { ...
代码清晰易懂,灵活可扩展,运行结果可预知。
flowers
的大小(这可能非常棘手难以检查!),那么就无法进行优化。 - Joachim Sauer