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

2018-07-19 17:32:23 +08:00
 vansl

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

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 = {};
2198 次点击
所在节点   科技
5 条回复
feiyuanqiu
2018-07-19 19:23:20 +08:00
『我发现 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 返回 true 』

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

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

更简单的方法是复制 ArrsyList 的源码到一个新文件,然后用新的 ArrayList 来 debug,排除其他调用的干扰
vansl
2018-07-19 20:24:35 +08:00
@xmh51
@feiyuanqiu
感谢二位!确实是这样的,我说怎么之前 debug 到构造方法里传进来的值也不对。看来知识面还是不够广。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/472425

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX