V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
vansl
V2EX  ›  科技

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

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

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

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = 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   ·   实用小工具   ·   5245 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 03:46 · PVG 11:46 · LAX 19:46 · JFK 22:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.