JDK1.8 的 ArrayList 的初始扩容机制问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
vansl
V2EX    科技

JDK1.8 的 ArrayList 的初始扩容机制问题

  •  
  •   vansl 2018-07-19 17:32:23 +08:00 2650 次点击
    这是一个创建于 2640 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这是指定初始容量的构造方法:

    public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.eementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } 

    这是增加元素的方法:

    public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } 

    这是用于扩容的部分代码:

    private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } 

    现在我执行如下代码:

    ArrayList list = new ArrayList(0); list.add(1); 

    流程是这样的:

    1. 首先会调用构造方法,由于初始容量是 0,这个构造方法把 EMPTY_ELEMENTDATA 赋给 elementData。
    2. 在 add 第一个元素之后,会执行 ensureCapacityInternal,然后调用 calculateCapacity 计算需求容量。

    问题出在第二步,我发现 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 返回 true,请问这是为什么? EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是两个不一样的对象:

    private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 
    5 条回复    2018-07-19 20:24:35 +08:00
    feiyuanqiu
        1
    feiyuanqiu  
       2018-07-19 19:23:20 +08:00
    『我发现 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 返回 true 』

    你怎么发现的?如果是用 IDE 打断点看的,注意打断点的位置
    vansl
        2
    vansl  
    OP
       2018-07-19 19:30:41 +08:00 via iPhone
    @feiyuanqiu
    debug 之后确认的,确实进入了 if 后面的语句。
    xmh51
        3
    xmh51  
       2018-07-19 19:44:15 +08:00   1
    @vansl 问题出现在 arraylist 是一个基础类。你打断点后看到的执行 calculateCapacity 并不是你传入的值的执行。不信你传入 2 试试? 断点应该打在 public boolean add(E e) {} 里面
    feiyuanqiu
        4
    feiyuanqiu  
       2018-07-19 19:48:00 +08:00 via Android   3
    @vansl
    如果用 IDE debug,不要直接在 add 方法处打断点,ArrayList 是个很基础的类,debug 启动过程很可能也会用到它

    在 add 方法调用处断点,程序运行到这时,再进 add 方法里面断点,就能看到结果了

    更简单的方法是复制 ArrsyList 的源码到一个新文件,然后用新的 ArrayList 来 debug,排除其他调用的干扰
    vansl
        5
    vansl  
    OP
       2018-07-19 20:24:35 +08:00
    @xmh51
    @feiyuanqiu
    感谢二位!确实是这样的,我说怎么之前 debug 到构造方法里传进来的值也不对。看来知识面还是不够广。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3459 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 00:18 PVG 08:18 LAX 17:18 JFK 20:18
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86