httpcore中发现了forEach和Iterator不可告人的关系!!!

赛利亚:今天又是充满希望的一天。

今天偶然进去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不一致,所以就抛出异常了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值