Java集合-ArrayList浅记录

文章详细对比了数组和集合的区别,重点解析了ArrayList的构造过程和扩容机制。当使用无参构造时,首次添加元素会初始化为长度为10的数组;有参构造时,若初始容量为0,扩容会变为1,否则按1.5倍增长。ArrayList没有length属性,使用size()获取元素数量。
摘要由CSDN通过智能技术生成

一.集合与数组的区别

1.数组

  • 静态的,固定数组长度(即创建之后无法改变),不会自动扩容
  • 只能存储单一数据
  • 增添删除比较麻烦,需要复制数组

 2.集合

  • 动态的,会自动扩容
  • 可以存储不同数组(引用类型一般只能存储一种,支持泛型),也可以存储不同子类,但是声明得为该子类的父类
  • 可以存储映射关系

3.ArrayList (源码浅赏析)

3.1 构造参数
  •  底层是由数组实现
  • // 调用有参构造时 传入的参数为0
     private static final Object[] EMPTY_ELEMENTDATA = {};
     // 调用无参构造时 默认赋值
     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
     // 底层维护了一个Object[]的数组
     transient Object[] elementData; // non-private to simplify nested class access
    
    
     // 有参构造
     public ArrayList(int initialCapacity) {
            // 如果传入的参数>0 即一开始的数组容量
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
            // 如果传入的 == 0
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    
    // 无参构造
    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
 3.2 扩容机制(重要

(1)无参构造

由上图我们可知,底层会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData,当我们第一次添加元素进去的时候,会调用

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

size为当前的List长度,该方法是用来判断是否需要扩容 

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

由上述可知,这里会直接返回DEFAULT_CAPACITY,观阅源码可知,该值为10

第一次add时候 minCapacity为10 很明显是大于elementData.length的

所以初始化为10

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

结论:

ArrayList无参构造时,第一次添加元素,会默认赋值长度为10的数组

 (2)有参构造

这里我们分为两种情况 initialCapacity = 0 和 initialCapacity > 0

我们先讲initialCapacity = 0的情况

同样,由第一张图我们可知,底层会将EMPTY_ELEMENTDATA赋值给我们的elementdata[] 

同样我们添加元素的时候,即调用add()的时候,也会判断是否扩容

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

当前size为0 所以我们传进去的值为1

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

注意:这里elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA才会扩容为10

很明显这里我们并不是,所以这里的minCapacity为1

同样我们也会进行扩容操作(这里我就直接放扩容部分源码了)

这里我们判断 newCapacity - minCapacity < 0 所以newCapacity被赋值为1

结果就是扩容为1的数组

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

从上图我们可以看出,每次扩容List都是1.5倍的扩容,再进行copyOf的方法,从名字我们可以得知是将旧数组复制到新数组上。

------------------------------------------------------------------------------------------------------------------------
接下来是initialCapacity > 0的情况:

假设我们new一个长度为3的ArrayList 底层实际上为

this.elementData = new Object[3];

添加元素的时候每次都会判断是否需要扩容 size此时为3

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

同样这里也不会走默认为10的数组中去 返回的 minCapacity为4

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

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

可知这里我们是需要扩容的

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

最终扩容为长度为 3 + 3/2 = 4
 

4.注意 

  • ArrayList没有length()方法,也没有length这个属性,源码中的length,其实是elementData的length
  • ArrayList只有size()来获取List中的长度。

本文借鉴:Java集合详解(含JDK8源码)_jdk8源码学习_汤姆&Tom的博客-CSDN博客

如果我理解的内容有误,烦请评论区留言或者私聊,欢迎各位来探讨~ 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值