文章分析了下面这两种for循环的写法
for (int i = 0; i < list.size(); i++) {
list.get(i);
}
for (String string : list) {
;
}
1、执行看效率
选用了两种有代表性的List,分别是ArrayList和LinkedList
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class DealForTest {
public void testArrayListFor() {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000000; i++) {
list.add("1");
}
long s = System.nanoTime();
for (int i = 0; i < list.size(); i++) {
list.get(i);
}
long e = System.nanoTime();
System.out.println("for def:" + (e - s));
s = System.nanoTime();
for (String str : list) {
;
}
e = System.nanoTime();
System.out.println("foreach:" + (e - s));
}
public void testLinkedListFor() {
List<String> list = new LinkedList<String>();
for (int i = 0; i < 10000000; i++) {
list.add("1");
}
long s = System.nanoTime();
for (int i = 0; i < list.size(); i++) {
list.get(i);
}
long e = System.nanoTime();
System.out.println("fordef:" + (e - s));
s = System.nanoTime();
for (String str : list) {
;
}
e = System.nanoTime();
System.out.println("foreach:" + (e - s));
}
public static void main(String[] args) {
DealForTest test = new DealForTest();
test.testArrayListFor();
}
}
testArrayListFor方法执行结果如下:
[root@li494-25 java]# java -Xms512m DealForTest
for def:216475470
foreach:220800083
[root@li494-25 java]# java -Xms512m DealForTest
for def:155585111
foreach:201191721
[root@li494-25 java]# java -Xms512m DealForTest
for def:171001760
foreach:199440627
[root@li494-25 java]# java -Xms512m DealForTest
for def:168588851
foreach:202388936
一共执行了4次,明显可以看出普通的for循环比foreach快了那么一点点
把test.testArrayListFor();改成test.testLinkedListFor();再执行
悲剧了,程序好像夯住了,我们用jstack看下会发现程序一直在执行
LinkedList.get
也就是代码里的
list.get(i);
"main" prio=10 tid=0xb6c05400 nid=0x3aed runnable [0xb6dee000]
java.lang.Thread.State: RUNNABLE
at java.util.LinkedList.node(LinkedList.java:569)
at java.util.LinkedList.get(LinkedList.java:475)
at DealForTest.testLinkedListFor(DealForTest.java:34)
at DealForTest.main(DealForTest.java:49)"VM Thread" prio=10 tid=0xb6c7a800 nid=0x3aee runnable
这说明大数据量的LinkedList的get根本没法用,注释掉代码里的list.get(i);再次执行如果如下:
[root@li494-25 java]# java -Xms512m DealForTest
fordef:108696577
foreach:262918732
[root@li494-25 java]# java -Xms512m DealForTest
fordef:81297371
foreach:239272835
[root@li494-25 java]# java -Xms512m DealForTest
fordef:80040708
foreach:238010609
[root@li494-25 java]# java -Xms512m DealForTest
fordef:79126135
foreach:283810388
一共执行4次,普通循环没法用,foreach虽然没有ArrayList的foreach快,但至少也能用了
2、为什么会这样?ArrayList的foreach为什么会慢、LinkedList普通for循环在大数据量时为什么不可用,而foreach可用?
反编辑下看看
编辑命令稍微变化一下:javac -g DealForTest.java 再使用javap -v DealForTest public void testArrayListFor(); flags: ACC_PUBLIC Code: stack=6, locals=8, args_size=1 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."":()V 7: astore_1 8: iconst_0 9: istore_2 10: iload_2 11: ldc #4 // int 10000000 13: if_icmpge 31 16: aload_1 17: ldc #5 // String 1 19: invokeinterface #6, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 24: pop 25: iinc 2, 1 28: goto 10 31: invokestatic #7 // Method java/lang/System.nanoTime:()J 34: lstore_2 35: iconst_0 36: istore 4 38: iload 4 40: aload_1 41: invokeinterface #8, 1 // InterfaceMethod java/util/List.size:()I 46: if_icmpge 64 49: aload_1 50: iload 4 52: invokeinterface #9, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 57: pop 58: iinc 4, 1 61: goto 38 64: invokestatic #7 // Method java/lang/System.nanoTime:()J 67: lstore 4 69: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 72: new #11 // class java/lang/StringBuilder 75: dup 76: invokespecial #12 // Method java/lang/StringBuilder."":()V 79: ldc #13 // String for def: 81: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 84: lload 4 86: lload_2 87: lsub 88: invokevirtual #15 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 91: invokevirtual #16 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 94: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 97: invokestatic #7 // Method java/lang/System.nanoTime:()J 100: lstore_2 101: aload_1 102: invokeinterface #18, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 107: astore 6 109: aload 6 111: invokeinterface #19, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 116: ifeq 134 119: aload 6 121: invokeinterface #20, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 126: checkcast #21 // class java/lang/String 129: astore 7 131: goto 109 134: invokestatic #7 // Method java/lang/System.nanoTime:()J 137: lstore 4 139: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 142: new #11 // class java/lang/StringBuilder 145: dup 146: invokespecial #12 // Method java/lang/StringBuilder."":()V 149: ldc #22 // String foreach: 151: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 154: lload 4 156: lload_2 157: lsub 158: invokevirtual #15 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 161: invokevirtual #16 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 164: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 167: return public void testLinkedListFor(); flags: ACC_PUBLIC Code: stack=6, locals=8, args_size=1 0: new #23 // class java/util/LinkedList 3: dup 4: invokespecial #24 // Method java/util/LinkedList."":()V 7: astore_1 8: iconst_0 9: istore_2 10: iload_2 11: ldc #4 // int 10000000 13: if_icmpge 31 16: aload_1 17: ldc #5 // String 1 19: invokeinterface #6, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 24: pop 25: iinc 2, 1 28: goto 10 31: invokestatic #7 // Method java/lang/System.nanoTime:()J 34: lstore_2 35: iconst_0 36: istore 4 38: iload 4 40: aload_1 41: invokeinterface #8, 1 // InterfaceMethod java/util/List.size:()I 46: if_icmpge 55 49: iinc 4, 1 52: goto 38 55: invokestatic #7 // Method java/lang/System.nanoTime:()J 58: lstore 4 60: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 63: new #11 // class java/lang/StringBuilder 66: dup 67: invokespecial #12 // Method java/lang/StringBuilder."":()V 70: ldc #25 // String fordef: 72: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 75: lload 4 77: lload_2 78: lsub 79: invokevirtual #15 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 82: invokevirtual #16 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 85: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 88: invokestatic #7 // Method java/lang/System.nanoTime:()J 91: lstore_2 92: aload_1 93: invokeinterface #18, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 98: astore 6 100: aload 6 102: invokeinterface #19, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 107: ifeq 125 110: aload 6 112: invokeinterface #20, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 117: checkcast #21 // class java/lang/String 120: astore 7 122: goto 100 125: invokestatic #7 // Method java/lang/System.nanoTime:()J 128: lstore 4 130: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 133: new #11 // class java/lang/StringBuilder 136: dup 137: invokespecial #12 // Method java/lang/StringBuilder."":()V 140: ldc #22 // String foreach: 142: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 145: lload 4 147: lload_2 148: lsub 149: invokevirtual #15 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 152: invokevirtual #16 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 155: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 158: return
3、结论
反编辑结果会发现foreach使用Iterator接口实现的,普通的循环直接使用List接口的get,这样就好理解了。
ArrayList底层是数组,通过下标访问自然是异常的快;通过Iterator接口则需要List.iterator:()、Iterator.hasNext:()和Iterator.next:()虽然最后取数据可是通过数组下标,但一连串的计算占用了一定时间,所以比起普通循环会慢上一些。
LinkedList是链表,用get就悲剧了,越往后越慢,取第10条数据还要把前9条数据溜一个遍(插入删除较多的、数据量较小的还是可用的),例子中1千万的数据量它肯定是玩不转的;LinkedList实现的Iterator接口是基于链表特性的,所以还是可用的,毕竟链表结构决定了它不是干这种活儿的。
编辑器弄不好了,原文地址