ArrayList源码解析

在这里插入图片描述
可以看到ArrayList 继承了 AbstractList
实现List,RandomAccess,Cloneable,序列化接口

怎么理解capacity和size
可以看到我们的初始容量为15
但是它的size为4
这就说明size指的是 实际存储了多少。
在这里插入图片描述
elementData: Object[]类型的动态数组

变量

	// 序列化ID
	private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 初始容量 10 
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空的元素数据  且是Obejct数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 默认容量 空的数组
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * elementData 里面存放的  Object数组
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 实际大小
     */
    private int size;
    /**
     * 数组最大 大小为  2^31-9
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造函数

   /**
     * 指定初始容量
     */
    public ArrayList(int initialCapacity) {
    	// 指定初始容量 > 0
        if (initialCapacity > 0) {
        	// 创建初始容量大小的元素
            this.elementData = new Object[initialCapacity];
            // 指定容量大小为0
        } else if (initialCapacity == 0) {
        	// 空的数据
            this.elementData = EMPTY_ELEMENTDATA;
            // 否则抛出异常
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * 无参构造器  数组中为空
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
	* 1)将collection对象转换成数组,然后将数组的地址的赋给elementData。
	* 2)更新size的值,同时判断size的大小,如果是size等于0,直接将空对象EMPTY_ELEMENTDATA的地址赋给elementData
	* 3)如果size的值大于0,则执行Arrays.copy方法,把collection对象的内容(可以理解为深拷贝)copy到elementData中。
	* 注意:this.elementData = arg0.toArray(); 这里执行的简单赋值时浅拷贝,所以要执行Arrays,copy 做深拷贝
    */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

前面一直对.length的概念有点混淆
特定做了一个测试:
发现length就是数组new出来的大小
如果在其中没有添加

在这里插入图片描述
那么Object类型里面 未添加元素的下表默认为null

在这里插入图片描述

很多方法的形参里面有minCapacity:
minCapacity:直译过来就是最小容量,即指我们添加元素后,或者自我指定容量的最小容量值。

扩容

 /**
  * trim实际大小:修整  实际大小
  * ArrayList的容量调整为实际元素的大小
  */
 public void trimToSize() {
 		// 记录List结构的修改次数 用于fast-fail
        modCount++
        //实际大小 < elementData数组长度
        if (size < elementData.length) {
        	// 实际大小为0?
        	// 为0  定义为空的elementData数组
        	// 不为0 我们就定义size长的elementData数组
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

    /**
     * 方法名:确定容量
     * 判断所需的ro
     * Explicit : 明确的,清晰的
     * minCapacity: 所需长度
	 * 总结:
	 * elementData数组里面为空  那么就执行ensureExplicitCapacity方法
	 * 里面不为空 但所需的长度大于默认容量10  执行ensureExplicitCapacity方法
     * 
     */
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

	/**
	 *  方法名:计算容量
	 *  数组默认为空 返回默认容量和我们所需容量中的最大值
	 *  数组不为null  返回所需容量
	 */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }


	/**
	 * 确定容量的内部
	 * internal: 内部的
	 * 
	 */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
	/**
	 * 确定扩容?
	 * explicit:明确的,清晰的
	 */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 所需容量 > 数组的长度 扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }



    /**
     *  真正的扩容
     */
    private void grow(int minCapacity) {
        // 旧容量
        int oldCapacity = elementData.length;
        // 新容量 = 旧容量 +旧容量*0.5 = 旧容量*1.5
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 新容量小于所需容量
        // 新容量 = 所需容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 新容量 》 最大数组大小  
        // 所需容量大于最大数组大小 新容量 = Integer的最大值
        // 所需容量小于最大数组大小 新容量 = 最大数组大小
        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);
    }
	/**
	 * huge:巨大的
	 */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

添加元素

在这里插入图片描述
这个方法其实和上面的add类似,该方法可以按照元素的位置,指定位置插入元素,具体的执行逻辑如下:
1)确保数插入的位置小于等于当前数组长度,并且不小于0,否则抛出异常
2)确保数组已使用长度(size)加1之后足够存下 下一个数据
3)修改次数(modCount)标识自增1,如果当前数组已使用长度(size)加1后的大于当前的数组长度,则调用grow方法,增长数组
4)grow方法会将当前数组的长度变为原来容量的1.5倍。
5)确保有足够的容量之后,使用System.arraycopy 将需要插入的位置(index)后面的元素统统往后移动一位。
6)将新的数据内容存放到数组的指定位置(index)上

    /**
     *  确认元素内部容量大小 所需容量为size+1
     *  e 赋值给 数组后一个下标
     *  返回true值
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    /**
     *  
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

ArrayList总结

底层数组实现,使用默认构造方法初始化出来的容量是10
扩容的长度是原长度的1.5倍
实现了RandomAccess接口,底层又是数组,get读取元素性能很好
线程不安全,所有的方法均不是同步方法也没有加锁,因此多线程下慎用
顺序添加很方便
删除和插入需要复制数组 性能很差(可以使用LinkindList)

为什么ArrayList的elementData是用transient修饰的?
transient修饰的属性意味着不会被序列化,也就是说在序列化ArrayList的时候,不序列化elementData。
为什么要这么做呢?

elementData不总是满的,每次都序列化,会浪费时间和空间
重写了writeObject 保证序列化的时候虽然不序列化全部 但是有的元素都序列化

Int和Integer的区别

Integer是一个包装类,而int是一个基础数据类型
在Integer有最大值和最小值

	// 二进制结果为0
    @Native public static final int   MIN_VALUE = 0x80000000;
	// 二进制结果为2147483647   2^31 -1 
    @Native public static final int   MAX_VALUE = 0x7fffffff;

==参考 原文:https://blog.csdn.net/fighterandknight/article/details/61240861 ==

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值