ArrayList源码解读

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList源码解读相关的知识,希望对你有一定的参考价值。

在端午节这个节日里,有一个特殊的任务,我带着你一起揭开“ArrayList”的真面目。从成员变量、构造函数、主要方法三部分,对ArrayList有进一步的认识,希望能够帮助你。

一、成员变量

   //默认容量
    private static final int DEFAULT_CAPACITY = 10;
    //空数组,当调用无参数构造函数的时候默认给个空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //真正保存数据的数组
    private transient Object[] elementData;
    //数组的大小
    private int size;

transient关键字:ArrayList实现了Serializable接口,而添加上transient关键字,则elementData对象就不可以序列化。

二、构造函数

  • 无参构造方法

  • 带有容量的构造方法

  • 带参数Collection的构造方法

在参数Collection的构造方法中,只有当当得到的elementData的类名不是Object,才会执行复制操作,因为Java是动态绑定,所以getClass获取的并不一定是Object类型,有可能是它的子类。

/**
     * 初始化elementData
     * @param initialCapacity
     */
    public ArrayList(int initialCapacity) {
        super();
        //如果小于0,则抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        //初始化elementData数组
        this.elementData = new Object[initialCapacity];
    }

    /**
     * 初始化为空的对象数组
     */
    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }
    
    public ArrayList(Collection<? extends E> c) {
        //将c容器转换为object类型的数组
        elementData = c.toArray();
        //更新size的大小
        size = elementData.length;
        //当得到的elementData的类名不是Object,才会执行复制操作
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

三、主要方法

1、add方法

//添加元素
    public boolean add(E e) {
        //首先判断数组容量是否需要扩充
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //添加元素,更新size的大小
        elementData[size++] = e;
        return true;
    }

ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时ensureCapacityInternal,才分配10个对象空间。

2、get方法

 //通过下标获取元素
    public E get(int index) {
        //判断下标的合理性
        rangeCheck(index);
        //返回元素
        return elementData(index);
    }

3、remove方法

在remove方法中,我们需要注意对null的特殊处理。

public boolean remove(Object o) {
        //如果为null对象,则特殊处理,使用==来判断
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            //如果不为null,则使用equals方法来判断。
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

四、其他常用的方法

removeAll:移除集合A和集合B的交集,即A-B。

retainAll:保留两个集合的交集。

五、ArrayList的大小是如何自动增加的?(面试题)

  •  ensureCapacityInternal    是判断是否要扩容的方法

  •  ensureExplicitCapacity

  •  grow 具体的扩容方法

实现思想:

  1. 确定所需minCapacity和数组当前size的关系,如果minCapacity>size,则准备grow。

  2. 如果1.5size<minCapacity,则准备扩充为minCapacity。

  3. minCapacity和MAX_ARRAY_SIZE 的关系,如果minCapacity>MAX_ARRAY_SIZE ,则需要判断minCapacity是否超出int类型所能表示的最大值。如果超出,则抛出OutOfMemoryError。

 //确保数组能够存放的:minCapacity
    private void ensureCapacityInternal(int minCapacity) {
        //如果elementData是空对象数组,最小容量为minCapacity和DEFAULT_CAPACITY最大值
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //判断是否需要扩容
        ensureExplicitCapacity(minCapacity);
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        //增加被改变的次数
        modCount++;
        // 最小容量大于elementData的长度,则需要扩充
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    //数组的最大上限
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    
    private void grow(int minCapacity) {
        // 数组原来的长度,保存副本
        int oldCapacity = elementData.length;
        //扩容至原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //判断新容量是否够用,如果不够则赋值为minCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果newCapacity超过MAX_ARRAY_SIZE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            //检查容量的int值是不是已经溢出。
            newCapacity = hugeCapacity(minCapacity);
        // 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间  
        // 并将elementData的数据复制到新的内存空间  
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    } 

欢迎加入Java学习交流群点击:484757838 

以上是关于ArrayList源码解读的主要内容,如果未能解决你的问题,请参考以下文章

源码阅读Java集合 - ArrayList深度源码解读

源码解读:ArrayList源码解析(JDK8)

ArrayList源码解读(jdk1.8)

ArrayList源码解读(部分)

arraylist源码解读

ArrayList源码解读