赛利亚:今天又是充满希望的一天。
今天偶然进去httpcore的源码中的HeaderGroup ,发现上面写着一些英格力士。
public class HeaderGroup implements Cloneable, Serializable {
private static final long serialVersionUID = 2608834160639271617L;
private static final Header[] EMPTY = new Header[0];
private final List<Header> headers = new ArrayList(16);
public void updateHeader(Header header) {
if (header == null) {
return;
}
// HTTPCORE-361 : we don't use the for-each syntax, i.e.
// for(Header header : headers)
// as that creates an Iterator that needs to be garbage-collected
for(int i = 0; i < this.headers.size(); ++i) {
Header current = (Header)this.headers.get(i);
if (current.getName().equalsIgnoreCase(header.getName())) {
this.headers.set(i, header);
return;
}
}
this.headers.add(header);
}
}
注释里说的是我们不使用forEach语法,它会创建Iterator 对象造成多余的GC。
难道是forEach会创建Iterator吗?我们都知道forEach中移除元素会出异常,但是这个还真没了解过,后来终于找到了apache的一个文档。
文档地址:https://www.mail-archive.com/dev@hc.apache.org/msg11619.html
里面写着uses a Java syntax that creates an iterator object for eac hcall. 翻译过来就是使用 Java 语法为每个调用创建一个迭代器对象。
然后做了一个小测试:
写一段使用forEach的代码。
import java.util.ArrayList;
import java.util.List;
public class Test{
public static void main(String[] args){
List <Integer> list = new ArrayList();
list.add(1);
list.add(2);
for (Integer num : list){
System.out.println(num);
}
}
}
执行javap -verbose Test > info.txt
命令,拿到反编译信息。
Classfile /C:/Users/jerrold/Desktop/forEach/Test.class
Last modified 2021-8-7; size 810 bytes
MD5 checksum c0e95ea535183603950fccfe46f2b808
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#25 // java/lang/Object."<init>":()V
#2 = Class #26 // java/util/ArrayList
#3 = Methodref #2.#25 // java/util/ArrayList."<init>":()V
#4 = Methodref #9.#27 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#5 = InterfaceMethodref #28.#29 // java/util/List.add:(Ljava/lang/Object;)Z
#6 = InterfaceMethodref #28.#30 // java/util/List.iterator:()Ljava/util/Iterator;
#7 = InterfaceMethodref #31.#32 // java/util/Iterator.hasNext:()Z
#8 = InterfaceMethodref #31.#33 // java/util/Iterator.next:()Ljava/lang/Object;
#9 = Class #34 // java/lang/Integer
#10 = Fieldref #35.#36 // java/lang/System.out:Ljava/io/PrintStream;
#11 = Methodref #37.#38 // java/io/PrintStream.println:(Ljava/lang/Object;)V
#12 = Class #39 // Test
#13 = Class #40 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 main
#19 = Utf8 ([Ljava/lang/String;)V
#20 = Utf8 StackMapTable
#21 = Class #41 // java/util/List
#22 = Class #42 // java/util/Iterator
#23 = Utf8 SourceFile
#24 = Utf8 Test.java
#25 = NameAndType #14:#15 // "<init>":()V
#26 = Utf8 java/util/ArrayList
#27 = NameAndType #43:#44 // valueOf:(I)Ljava/lang/Integer;
#28 = Class #41 // java/util/List
#29 = NameAndType #45:#46 // add:(Ljava/lang/Object;)Z
#30 = NameAndType #47:#48 // iterator:()Ljava/util/Iterator;
#31 = Class #42 // java/util/Iterator
#32 = NameAndType #49:#50 // hasNext:()Z
#33 = NameAndType #51:#52 // next:()Ljava/lang/Object;
#34 = Utf8 java/lang/Integer
#35 = Class #53 // java/lang/System
#36 = NameAndType #54:#55 // out:Ljava/io/PrintStream;
#37 = Class #56 // java/io/PrintStream
#38 = NameAndType #57:#58 // println:(Ljava/lang/Object;)V
#39 = Utf8 Test
#40 = Utf8 java/lang/Object
#41 = Utf8 java/util/List
#42 = Utf8 java/util/Iterator
#43 = Utf8 valueOf
#44 = Utf8 (I)Ljava/lang/Integer;
#45 = Utf8 add
#46 = Utf8 (Ljava/lang/Object;)Z
#47 = Utf8 iterator
#48 = Utf8 ()Ljava/util/Iterator;
#49 = Utf8 hasNext
#50 = Utf8 ()Z
#51 = Utf8 next
#52 = Utf8 ()Ljava/lang/Object;
#53 = Utf8 java/lang/System
#54 = Utf8 out
#55 = Utf8 Ljava/io/PrintStream;
#56 = Utf8 java/io/PrintStream
#57 = Utf8 println
#58 = Utf8 (Ljava/lang/Object;)V
{
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 5: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
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: iconst_1
10: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: aload_1
20: iconst_2
21: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
24: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
29: pop
30: aload_1
31: invokeinterface #6, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
36: astore_2
37: aload_2
38: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
43: ifeq 66
46: aload_2
47: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
52: checkcast #9 // class java/lang/Integer
55: astore_3
56: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
59: aload_3
60: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
63: goto 37
66: return
LineNumberTable:
line 7: 0
line 9: 8
line 10: 19
line 12: 30
line 13: 56
line 14: 63
line 15: 66
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 37
locals = [ class java/util/List, class java/util/Iterator ]
frame_type = 250 /* chop */
offset_delta = 28
}
SourceFile: "Test.java"
在Constant pool的 #6 #7 #8可以看到Iterator的影子。也就是在对有实现Iterable接口的对象采用foreach,编译器会将这个for关键字转化为对目标的迭代器使用。反编译过来的代码就是以下的意思。
import java.util.ArrayList;
public class Test {
public static void main(String[] paramArrayOfString) {
ArrayList<Integer> arrayList = new ArrayList();
list.add(1);
list.add(2);
for (java.util.Iterator i$ = list.iterator(); i$.hasNext();){
Integer integer = (Integer) i$.next();
System.out.println(integer);
}
}
}
httpcore中的HeaderGroup 使用的是header的ArrayList集合,ArrayList实现了List接口,List接口继承了Collection接口,而Collection接口又继承了Iterable接口。这也就可以解释HeaderGroup 为什么不用forEach语法来遍历了。
这时也可以解释为什么在forEach中对List使用remove方法会抛异常了,当我们知道实现Iterable接口的对象会转换为iterator操作时,使用迭代器来进行遍历,这时迭代器会生成一个expectedModCount参数来记录修改元素的次数,当遍历的时候进行移除元素时,list集合的modCount和expectedModCount不一致,所以就抛出异常了。