ArrayList list =new ArrayList(20) 踩坑


问:ArrayList list =new ArrayList(20) 扩容了几次?

答案:0 次。

当第一眼看到这个问题,大脑下意识的会想到 ArrayList 集合的的 初始容量扩容机制;想到再深一点的就会联想到HashMap的初始容量和扩容机制。

但坑,就坑在这点。当你下意识想到 ArrayList 怎么进行扩容时,就已经陷入了这道题的陷阱了。

一想到扩容,就会对比 ArrayList 的初始容量,初始容量是10,然后每次需要扩容的时候就按照当前容量的 1.5 倍来分配新的空间,也就是 扩容后是原来容量的1.5倍。接着 20 比 10大,按照10*1.5=15,15 * 1.5 取 22 个,是不是就需要扩容两次?错,大错特错。

这当中还涉及到了jdk发展的版本问题。
  • 在JKD1.6中,如果通过无参构造的话,初始数组容量为10。

每次按照1.5倍(位运算)的比率通过copeOf的方式扩容。以上就是动态扩容的原理。

  • 在JDK1.7中,如果通过无参构造的话,初始数组容量为 0,当真正对数组进行添加元素时,才真正分配容量默认的初始容量。

我们通常讲集合扩容,往往会忽略一个前提,就是对集合空间进行扩容的前提是:集合中的已经存储的元素个数达到了扩容的条件,然后再去触发扩容机制

那回到 ArrayList。这里就讲 jdk1.7之后。

  1. New ArrayList():是什么?调用的 无参构造 方法,创建一个空的集合,这个过程称为“初始化”。

    	public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    

    这是无参构造,默认赋值初始容量的操作,elementData就是用来处理值的扩容操作等等。

    这时候属于没有指定某一个空间大小,jdk会给一个默认的初始容量,也就是 0。

    在没有往空的集合中添加元素之前,它分配的空间大小是 0;

    当第一次插入元素时才给分配10(默认)个对象空间。当添加第16个数据时,触发扩容机制,继续扩容变为15 * 1.5 =22个;

  2. New ArrayList(20);是什么?调用 有参构造 方法,创建一个 容量为20 的集合,这个过程也称为“初始化”。

        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);
            }
        }
    

    这就是直接定义好了初始容量20,此时也并没有元素,更没有达到触发扩容条件需要的元素个数。所以也就没有发生扩容。

            List<String> list = new ArrayList<>(11);
            list.add("1");
            list.add("1");
            list.add("1");
            list.add("1");
            list.add("1");
            list.add("1");
            list.add("1");
            list.add("1");
            list.add("1");
            list.add("1");
            list.add("1");
            // 此时给 list 集合的11个空间中存储了元素.当再次调用 add()方法添加元素时,就会触发扩容机制
            // 触发扩容机制后,11*1.5 取 16。此时给list分配的空间大小为 16,然后 将要添加的第 12 个元素就放在索引为11的位置
            list.add("12");
    

    JDK1.7以后ArrayList list =new ArrayList() 无参构造的话,初始数组容量为0的,在真正添加数据到数组里,才会调用add()/addAll()方法并真正分配其容量,按照1.5的比率去扩容。就是初始10个元素容量的数组,当给到第11个数据时,就会扩容到15,22等等。 就是按Arrays.copyOf(elementData, newCapacity)这个方式去实现的。

JAVA-技能点
知识点
Git/Github