如何解决用于循环优化
|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
运行时环境(构建
1.6.0_22-b04)Java HotSpot(TM)客户端VM(内部版本17.1-b03,混合模式,
分享)
因此,如果您需要从上述两个中进行选择,请争取第二名,但我个人会选择for-each
。
每个绩效
摘自Joshua Bloch撰写的有效Java的条款46:
for-each循环,在中引入
发布1.5,摆脱混乱
和错误的机会
隐藏迭代器或索引变量
完全。结果成语
同样适用于集合
数组:
// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
doSomething(e);
}
当您看到冒号(:)时,将其读为
因此,上面的循环读为
“对于元素中的每个元素e。”注意
没有性能损失
使用for-each循环,甚至
数组。实际上,它可能会提供一些
性能优于普通
在某些情况下for循环
计算数组索引的限制
只有一次。虽然您可以通过
手(项目45),程序员不会
总是这样做。
也可以看看
一个循环和每个循环之间有一个性能差异
,很抱歉,@ Jigar的回答不正确。这是正确的答案。 (tldr;请勿使用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做什么和不做什么。
,JVM无法优化它,因为size()
是一种方法,并且JVM无法(也不会尝试)确定ѭ13always在此上下文中将始终返回相同的值。假设size()
的值不变,第二个的性能会略高一些,但是增益却是如此,以至于您根本不必考虑使用它。
,如果性能至关重要,请使用普通计数器循环,但是对于98%的情况,代码的清晰度和简洁性更为重要(例如1000x或更多),因此应使用for-each循环。
@David指出使用计数器的速度更快,但我要指出的是,即使是10,000个条目,其差异也将达到亚微秒。
如果您有超过10,000个条目的集合,则很可能不应该对每种可能性都进行强力迭代。无论您有什么想法,像Map这样具有查找功能的集合更有可能是一个更好的解决方案。如果您的条目少于10,000个,那么性能就不太重要了。
,如果数组列表在迭代时更改,则行为会有所不同。但是我猜你不这样做。根据我的测试,后者通常更快(特别是在Android之类的系统上)。我将其编写如下:
for (int i = 0,size = flowers.size(); i < size; i++) {
...
}
,根据Java语言规范(14.14.1):
基本的for语句执行一些初始化代码,然后执行
表达式,语句和一些更新代码重复执行,直到
表达式为假。
在第一个示例中,表达式为i < flowers.size()
,并且在每次迭代中都对其求值一次。在您的特殊情况下,它应该没有明显的区别,因为ѭ19上的flowers.getSize()
是很短的方法。但是,总的来说,如果表达式的结果对于每次迭代都是相同的,并且代价昂贵,则进行预计算。
结果:在Java虚拟机的每个实现中都必须产生相同的输出,并证明Expression在每个迭代中都被评估一次:
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++ ]
您的结果将根据JVM和-client vs -server等其他设置而有所不同
因为某些测量是如此之小,所以您需要使用纳秒级的时间进行测量,并且您需要进行许多测试,否则最终会导致GC弄乱结果。同样,这些类型的测试也具有JVM将循环优化为零的习惯。我试图通过将在代码末尾修改的变量放到屏幕上来消除这种风险。
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);
}
,这仅是通过示例来说明情况如何。
我测试了循环“ 25”的“ normal”执行和优化了循环“ 26”的微执行。我从命令行在Eclipse中运行了测试,并注意到了巨大的差异。
在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
因此,在eclipse中,两个for循环花费相同的时间,但是从命令行运行时,原始版本比微优化版本花费800%的时间。差异的幅度令人震惊。我想eclipse使用了不同的JVM,该JVM应用了一些智能优化技巧。
但是,这并不意味着您应该开始使用微优化版本。在几乎所有情况下,您要遍历的列表可能很小,因此性能差异可以忽略不计。而且,使用标准版本所获得的可读性比每个人都可以更快地认识和理解,这比不明显的性能提升更有利。
为了完整起见,这是我运行的代码:
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,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;
}
}
,此外,如果您想知道将方法调用用作源集合是否会对性能产生任何影响。也就是说-该方法将被调用多次-答案是否定的。这是一个例子:
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
因此,该方法将仅被调用一次。
,我的方法在这个问题上有点不同。对我来说,选择哪种方法都没关系。原因是您将获得“最佳性能”的最佳优化方法是250万次迭代〜50ms! (根据@David \的帖子)。显然,这种改进并不是您想浪费宝贵的时间来寻找优化的解决方案。
(但是,按照OP最初的问题,我也想建议最后一种方法。)
我知道答案有点怪异和罕见,但这是事实。
,为了避免在编写代码时进行所有这些编号,迭代和检查,请使用以下具有最强性能的简单易读代码。为什么这具有最佳性能(细节即将出现)
for (Object object : aCollection) {
// Do something here
}
如果需要索引,则:
在以上两种形式之间进行选择:
第二个更好,因为您正在使用局部变量进行检查。当退出该方法时,变量将进入堆栈的垃圾箱。
,简单而有效
for (ConfigDataModel.VisaTypesBean.AddedBean visatype : visaTypesBeans) {
if (visatype.getId() == 24) {
}
,任一个都会做。根据JVM的不同,第二个可能会快几个时钟周期,但这将是不可估量的或微不足道的差异。注意这些类型的子优化。除非您要构建一个实时系统,每个CPU滴答声都在其中计数,否则它们只会增加复杂性并增加错误来源。
我建议使用迭代器构造(正如已经建议的那样)
for (Flower flower: flowers) { ...
它清晰,灵活且可预测。
,只去第一个,因为两者相同,但是在第二个代码片段中创建了一个多余的int变量。
所以去第一个代码片段
for (int i = 0; i < flowers.size(); i++) {
...
}
,String d = JOptionPane.showInputDialog(\"enter start\");
int s = Integer.parseInt(d);
String h = JOptionPane.showInputDialog(\"enter the end\");
int z = Integer.parseInt(h);
for (int a = 1 ; a<10 ; a++) {
if (a%2 == 0 ) {
JOptionPane.showMessageDialog(null,a);
System.out.print(a);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。