ArrayList 源码分析

功能简介

基于 jdk1.8。ArrayList 是一个动态可变的元素集合,按照添加顺序并且允许容器的元素为 null 值。其底层是使用数组实现的,因此其具有数组数据结构的特性:查询效率高,动态修改效率低。

核心参数

    // 默认初始化容量  
    private static final int DEFAULT_CAPACITY = 10;
    // 默认的空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 默认的空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 真实存放数组元素的数组
    transient Object[] elementData;
    // 元素容量
    private int size;

构造函数

ArrayList 共有 3 个构造函数

   // 指定容量的构造函数。 
   public ArrayList(int initialCapacity) {
        // 如果入参大于0,则创建个指定入参的数组集合
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        // 如果入参等于0,则使用空元素数组集合 
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            // 如果入参小于0,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    // 默认构造函数
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    // 通过传递其它集合对象,构造 ArrayList
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // defend against c.toArray (incorrectly) not returning Object[]
            // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

这里有个小问题。为什么空元素结合要设计两个?参考内容 中提到,其实 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是为了设置默认数组元素为 10 ,其采用了懒加载的方式,虽然一开始是空元素集合,但是首次添加元素时,会设置为 10,而 EMPTY_ELEMENTDATA 空元素集合就真的表示是数组元素为 0 的元素集合了,所有的空元素集合都可以使用这个数组。

核心方法

添加元素 && 扩容

    // 在元素末尾添加元素
    public boolean add(E e) {
        // 确保数组容量可放入元素,否则进行扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将元素 e 赋值给数组的 size 索引位置
        elementData[size++] = e;
        // 返回添加成功
        return true;
    }
    // 在指定位置添加元素
    public void add(int index, E element) {
        // 数组越界检查
        rangeCheckForAdd(index);
        // 确保数组容量可放入元素,否则进行扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将数组中 包括 index 位置以及后面的元素往数组后面移动一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 将元素 e 赋值给数组的 index 索引位置
        elementData[index] = element;
        // 数组容量增加
        size++;
    }    
		// 核心方法
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    // 如果是默认构造函数构造的 ArrayList,返回默认容量 10 或者 minCapacity
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    // 如果当前数组容量小于 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;
				// 默认扩容为 1.5 倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果 扩容后,依然小于 minCapacity,则直接扩容为 minCapacity
        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);
    }

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

  

    public E remove(int index) {
        // 判断数组是否越界
        rangeCheck(index);
      
        modCount++;
        // 获取元素
        E oldValue = elementData(index);
        // 将 index 及其之后的元素复制到 index 位置
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 将移动之后空出来的位置置为 null,移出来的元素就会被垃圾收集器清除
        elementData[--size] = null; // clear to let GC do its work
    
        return oldValue;
    }
    // 移除为 o 的元素
    public boolean remove(Object o) {
        // 如果 是 null,则遍历移除首个为 null 的元素
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            // 如果 是 o,则遍历移除首个为 o 的元素
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

	  // 清除所有元素
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }
		// 删除给定集合的元素
		public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
	  // 批量移除
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            // 这个好经典的算法,判断一个集合是否拥有另一个集合的某个元素,根据 complement 来判断是否保留还是删除。
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            // 删除不在集合内的元素
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }
  • batchRemove 里面有个经典判断两个集合是否包含某元素的算法。

    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

清空

    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

包含


    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    // 返回首个匹配的元素的索引,找不到返回 -1
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

判空

public boolean isEmpty() {
    return size == 0;
}

总结

  • ArrayList 底层采用数组结构,是线程不安全的,允许 null 值存在
  • ArrayList 中,修改和查询是高效的,新增和删除会导致数组的复制操作,所以效率比较低
  • ArrayList 可以指定初始容量,默认是 10,扩容是原先容量的 1.5 倍,或者在增加集合时,取 1.5 倍或者满足集合数量的差值
posted @ 2022-05-29 23:21  莫己若  阅读(24)  评论(0)    收藏  举报