【Java集合】ArrayList源码解析

ArrayList简介

ArrayList实现了AbstractList类和List接口,是基于数组实现的,是一个动态数组,其大小可以自动增长。所以它具备了数组的优势,可以通过元素索引快速查询,且是有序存储。

ArrayList不是线程安全的,多线程下可以考虑使用concurrent包下面的CopyOnWriteArrayList类。

同时实现了Serializable接口,因此支持序列化,能够通过序列化传输;实现了RandomAccess接口,支持快速随机访问;实现了Cloneable接口,能被克隆。
在这里插入图片描述

源码分析

核心实现其实就是通过数组底层实现,实现了数组的动态扩容。
在这里插入图片描述

具体是怎么实现的,通过源代码来看一下。

主要的成员变量:

/**
  	//默认的数组初始化容量
    private static final int DEFAULT_CAPACITY = 10;
    //定义一个空数组实例,以便其他需要的地方使用
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //定义一个空数组,跟前面的区别就是这个空数组是用来判断ArrayList第一添加数据的时候要扩容多少。默认的构造器情况下返回这个空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //ArrayList数据存储的数组,ArrayList的容量就是数组的长度。使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA构造实例时,第一次添加数据数组容量是10.
    transient Object[] elementData; 
    //ArrayList的size,即元素的个数。
    private int size;

构造函数:

	//构造一个指定初始容量的空列表
    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);
        }
    }
   //构造一个初始容量为10的空列表
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    //构造一个包含指定集合的元素的列表,按照集合的迭代顺序进行存储
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

可以看到总有三种构造方法,第一种指定了初始容量,第二种默认构造方法,初始化了容量为10的数组,第三种初始化带数据的ArrayList。

我们可以看到默认的构造器,通过DEFAULTCAPACITY_EMPTY_ELEMENTDATA返回了一个空的数组,当第一次有数据添加时,会自动扩容一个容量为10的数组,

那么扩容是怎么实现的呢?

扩容机制:
当每次调用add或者addAll进行数据添加时都会先检查是否需要扩容,进入ensureCapacityInternal方法:

扫描二维码关注公众号,回复: 8558402 查看本文章
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

所以默认构造函数,当第一次添加数据时,会初始化容量为10的数组。如果不是,继续往下走,如果容量不够,则需要调用grow方法进行扩容:

    private void ensureExplicitCapacity(int minCapacity) {
    	//快速报错机制
        modCount++;
        //如果容量不够,则需要调用grow方法进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

ArrayList扩容的核心方法grow():

  1. 调用默认构造函数,第一次添加数据时,会先扩容容量为10,后续再根据newCapacity = oldCapacity + (oldCapacity >> 1)进行扩容,即1.5倍大小进行扩容;
  2. 调用指定容量初始化并且初始化容量为0时,我能可以看到,前4次扩容都只能+1,第5次开始才进行1.5倍的扩容,所以不要指定初始化容量为0;
  3. 当扩容量newCapacity大于ArrayList数组定义的最大值后会调用hugeCapacity来进行判断。如果minCapacity已经大于Integer的最大值(溢出为负数)那么抛出OutOfMemoryError(内存溢出),否则的话根据与MAX_ARRAY_SIZE的比较情况确定是返回Integer最大值还是MAX_ARRAY_SIZE。
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
        	//如果数组长度超过
            newCapacity = hugeCapacity(minCapacity);
        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;
    }

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

思考

关于ArrayList源码,比较重要的几点:

  1. 注意三个不同的构造方法。无参构造方法构造的ArrayList当第一次添加数据时会扩容容量为默认10,带有Collection参数的构造方法,将Collection转化为数组赋给ArrayList的实现数组elementData。
  2. 从上面的扩容过程可以看出,每次扩容完都需要将原来的元素拷贝到一个新的数组上,非常耗时,所以建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList。
  3. ArrayList基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量移动元素,插入删除效率低。
发布了23 篇原创文章 · 获赞 49 · 访问量 4989

猜你喜欢

转载自blog.csdn.net/wudingmei1023/article/details/103943854