ArrayList源码学习

一.介绍

  • ArrayList是个动态数组,实现了List接口,是用来存储数据的容器之一,底层的数据结构是数组。
  • 如果存储基本类型的数据,如int,long,boolean,short,byte,那只存储它们对应的包装类。
  • ArrayList是线程不安全的。
  • 增删慢、查询快

二.源码部分

1.基本参数

//RandomAccess:随机快速访问接口
//Cloneable是标记型的接口,它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出CloneNotSupportedException
//Serializable:对象的序列化处理
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
    //序列化唯一标示 serialVerisionUID
    private static final long serialVersionUID = 8683452581122892189L;
    //默认容量 10 字节
    //static和final是控制类成员变化的修饰符。
    //使用final关键字可以声明类、成员变量和成员方法,一经声明,便不可继承、不可修改和不能覆盖
    private static final int DEFAULT_CAPACITY = 10;
    //空数据数组
    private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
    //默认空容量的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
    //实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
    transient Object[] elementData;
    private int size;
    //最大数组长度
    private static final int MAX_ARRAY_SIZE = 2147483639;

2.构造函数

    /**
     * 构造函数
     * @param initialCapacity
     */
    public ArrayList(int initialCapacity) {
        //如果传入的参数大于0,则构造一个参数大小的数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else {
            //如果传入参数小于0,则抛出异常
            if (initialCapacity != 0) {
                throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
            }
//参数为0,创造一个空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }

    }
//无参构造函数,构造一个初始容量为0的数组
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
//?:任意类
    public ArrayList(Collection<? extends E> c) {
        //toArray() 方法将 Arraylist 对象转换为数组
        Object[] a = c.toArray();
        //如果传入数组的长度不为0,
        if ((this.size = a.length) != 0) {
            //判断数组类型是否为object
            if (c.getClass() == ArrayList.class) {
                //是的话,将其指向传进来的数组
                this.elementData = a;
            } else {
                //转换为object对象
                this.elementData = Arrays.copyOf(a, this.size, Object[].class);
            }
        } else {
            //长度为0则数组为空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }

    }

3.扩容机制

      /**
     * arraylist扩容机制
     * arraylist有三种构造函数:无参,用户指定大小和指定集合元素的列表
     * 无参:初始化为空数组,当调用时扩容为10
     * @param minCapacity
     */
    //ensureCapacity() 方法用于设置具有指定容量大小的动态数组。
    //minCapacity - 动态数组的容量 
    public void ensureCapacity(int minCapacity) {
        //1`minCapacity > this.elementData.length 设置的容量大于数组容量
        //2`this.elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA 不为无参构造函数
        //3.minCapacity > 10
        if (minCapacity > this.elementData.length && (this.elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA || minCapacity > 10)) {
            ++this.modCount;
            this.grow(minCapacity);
        }

    }
//数组扩容
    private Object[] grow(int minCapacity) {
        //copyOf()方法传回的数组是新的数组对象
        //copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值
        return this.elementData = Arrays.copyOf(this.elementData, this.newCapacity(minCapacity));
    }

    private Object[] grow() {
        return this.grow(this.size + 1);
    }

    private int newCapacity(int minCapacity) {
        int oldCapacity = this.elementData.length;
        //新的容量相当于原来的原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //数组size>=4进入下面函数
        if (newCapacity - minCapacity <= 0) {
            //若arraylist为默认空数组,返回10或所需值
            if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(10, minCapacity);
            } else if (minCapacity < 0) { //所需值小于0,抛出异常
                throw new OutOfMemoryError();
            } else {
                return minCapacity;
            }
        } else {
            //检查新容量是否大于最大容量
            return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
        }
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) {
            throw new OutOfMemoryError();
        } else {
            //判断所需容量是否在指定范围内
            return minCapacity > 2147483639 ? 2147483647 : 2147483639;
        }
    }

4.add方法

//arraylist.add(int index,E element)
    private void add(E e, Object[] elementData, int s) {
        //如果数组的长度满了,扩容
        if (s == elementData.length) {
            elementData = this.grow();
        }
         //赋值
        elementData[s] = e;
        //size+1
        this.size = s + 1;
    }
//.add(E element)时
    public boolean add(E e) {
        //记录了ArrayList结构性变化的次数
        ++this.modCount;
        //在arraylist最后插入
        this.add(e, this.elementData, this.size);
        return true;
    }
//.add(int index,E element)元素插入指定位置
    public void add(int index, E element) {
        //add操作越界检查
        this.rangeCheckForAdd(index);
        ++this.modCount;
        //?
        int s;
        Object[] elementData;
        //扩容检查
        if ((s = this.size) == (elementData = this.elementData).length) {
            elementData = this.grow();
        }
        //index后的数据复制过来
        System.arraycopy(elementData, index, elementData, index + 1, s - index);
        //插入
        elementData[index] = element;
        this.size = s + 1;
    }
    /**
     * public static void arraycopy(
     *                              Object src,  //源数组
     *                              int srcPos,  //源数组的起始位置
     *                              Object dest, //目标数组
     *                              int destPos, //目标数组的起始位置
     *                              int length   //复制长度
     *                              )
     **/

5.其他方法

//缩小长度
    public void trimToSize() {
        //modCount是这个list被结构性修改的次数
        ++this.modCount;
        if (this.size < this.elementData.length) {
            //copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值
            this.elementData = this.size == 0 ? EMPTY_ELEMENTDATA : Arrays.copyOf(this.elementData, this.size);
        }

    }

//indexOf ()的意思:查找一个字符串中,第一次出现指定字符串的位置。
    public boolean contains(Object o) {
        return this.indexOf(o) >= 0;
    }

    public E get(int index) {
        //index是否被包含于[0,length),true返回值为:Index
        Objects.checkIndex(index, this.size);
        return this.elementData(index);
    }

    public E set(int index, E element) {
        //越界检查
        Objects.checkIndex(index, this.size);
        E oldValue = this.elementData(index);
        this.elementData[index] = element;
        return oldValue;
    }

三.总结

  1. EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的异同

​ 1)DEFAULTCAPACITY_EMPTY_ELEMENTDATA:无参构造函数创建,当第一次执行add操作时

数组扩容为10

​ 2)EMPTY_ELEMENTDATA则加1

  1. ensureCapacity(int minCapacity)方法:

1)当设置的minCapacity>容量且不是无参构造函数构造的数组或者minCapacity>10,才会去调用扩容的方法

2)所以当数组为DEFAULTCAPACITY_EMPTY_ELEMENTDATA时用这个方法要设置的值>10才会实际扩容

  1. add相关

1)有参构造函数数组,容量大于4开始进行1.5倍扩容


posted @ 2022-02-19 13:30  ftfty  阅读(28)  评论(0编辑  收藏  举报