ArrayList<String> strings = new ArrayList<String>(){{
add("a");
add("b");
add("c");
add("d");
}};
int size = strings.size();
这是我想知道哪一个使用的部分以及原因。
for(int i = 0; i < size; i++){
String temp_string = strings.get(i);
someMethod(temp_string);
}
或
for(int i = 0; i < size; i++){
someMethod(strings.get(i));
}
是否会产生任何性能差异? temp_string
是否指向保存在内存的arraylist部分中的String,或者它是否在每个循环的内存中分配另一个空格来保存temp_string
?
答案 0 :(得分:2)
如果它有所不同,我会感到惊讶,不仅因为在堆栈上创建临时引用然后将其复制到函数调用参数的成本非常低,而且主要是因为没有严重的编译器会错过明显的优化。
但是,如果没有测试,你无法确定。
答案 1 :(得分:1)
在两种情况下都可以帮助您了解正在发生的事情,看看您应该期待什么样的差异:
someMethod
,然后让临时变量超出范围someMethod
。从这里你应该立即得出结论,如果存在任何差异,那么优势就是在实现方面做更少的事情,即没有临时变量的实现。
在某些情况下,制作临时变量可以帮助您提高性能。以下是一个示例,基于您的代码段:
for(int i = 0; i < size; i++){
String temp_string = strings.get(i);
for (int j = 0 ; j != 10000 ; j++) {
someMethod(temp_string, 2*j+1);
}
}
此代码段会重复使用strings.get(i)
一万次的结果,因此当someMethod
非常快时,您可能会看到一些差异。但是,这不是一个保证的胜利:你需要进行分析,看看变化是否有所作为。
可能对快速someMethod
实施和长strings
列表产生影响的另一个变化是foreach
循环:
for(String s : strings){
someMethod(s);
}
当您不需要在循环内使用索引时,这是适用的。代码可能会快一点,因为在内部它可以避免在数组列表中进行索引检查。
答案 2 :(得分:0)
首先,我相信第二个算法你的意思是strings.get(i)not temp_string.get(i)因为没有声明temp_string。
现在,第一种算法需要更长的时钟周期。请注意,您具有所有相同的调用以及分配操作。在大多数情况下,这可以忽略不计,但在高性能环境中,这可能会很昂贵。
答案 3 :(得分:0)
temp_string
的范围在两种情况下都是相同的,即仅限于for循环。生成的字节代码几乎相同。
public void foo1(List<String> list) {
for (int i=0; i<list.size(); i++) {
String str = list.get(i);
someMethod(str);
}
}
public void foo2(List<String> list) {
for (int i=0; i<list.size(); i++) {
someMethod(list.get(i));
}
}
public void foo1(java.util.List);
Code:
0: iconst_0
1: istore_2
2: iload_2
3: aload_1
4: invokeinterface #2, 1; //InterfaceMethod java/util/List.size:()I
9: if_icmpge 34
12: aload_1
13: iload_2
14: invokeinterface #3, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
19: checkcast #4; //class java/lang/String
22: astore_3
23: aload_0
24: aload_3
25: invokevirtual #5; //Method someMethod:(Ljava/lang/String;)V
28: iinc 2, 1
31: goto 2
34: return
public void foo2(java.util.List);
Code:
0: iconst_0
1: istore_2
2: iload_2
3: aload_1
4: invokeinterface #2, 1; //InterfaceMethod java/util/List.size:()I
9: if_icmpge 32
12: aload_0
13: aload_1
14: iload_2
15: invokeinterface #3, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
20: checkcast #4; //class java/lang/String
23: invokevirtual #5; //Method someMethod:(Ljava/lang/String;)V
26: iinc 2, 1
29: goto 2
32: return
与第一个循环(分配给String,然后将String传递给方法)的唯一区别是额外的astore_3
和aload_3
。这应该很容易让JVM进行优化。
另请注意,第二个循环(无额外变量赋值)更易于阅读。