ArrayList

ArrayList源码阅读

一、基本介绍

2020/8/2

2020年3月份开始学习java,由于疫情原因在家上的网课,与其说是上网课,更多的其实是自学。

直到前天java基础知识终于学完了,肯定有很多大佬觉得这速度有点慢。确实,我也觉得慢,因为6月初到7月初我都在准备期末考试,7月初到7月中旬,遇到了一件很不愉快的事情,也无心学习。不过后问题解决后,我又重新找回了学习的状态,继续肝java。

说起看jdk源代码的起因也是很搞笑,因为基础知识学完了,有点骄傲,又有点彷徨,不知道下一步该干啥,所以我就去洛谷刷题。但是我的算法真的学的太差了,一个简单的暴力搜索,愣是一个半小时没写出来,一直写到自闭,写到怀疑人生。所以,今天我决定阅读jdk的源代码,就从我常用的ArrayList集合开始吧。

二、成员变量(类的属性)

1、DEFAULT_CAPACITY

​ ArrayList的默认容量,该变量被final修饰,是私有不可变的。

 private static final int DEFAULT_CAPACITY = 10;

2、EMPTY_ELEMENTDATA

​ 用于空实例的共享数组。

private static final Object[] EMPTY_ELEMENTDATA;

3、DEFAULTCAPACITY_EMPTY_ELEMENTDATA

​ 用于默认大小的空实列共享数组。

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

4、elementData

​ 用于存储ArrayList元素的数组缓冲区。(具体数据就存储在这个位置)

transient Object[] elementData;

5、size

​ ArrayList对象的有效数据长度。

private int size;

6、MAX_ARRAY_SIZE

​ 要分配的数组的最大大小(除非必要)。

​ 一些虚拟机在数组中保留一些标题字。

​ 尝试分配更大的数组可能会导致

​ OutOfMemoryError:请求的数组大小超出了VM限制

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

三、构造函数

无参构造函数
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
无参构造函数就必须举一个例子:
ArrayList<Integer> list = new ArrayLIst<>();
以上是我们常用的创建ArrayList对象的语法,调用的就是无参构造方法。调用时,会将成员变量Object[ ] = DEFAULTCAPACITY_EMPTY_ELEMNTDATA,  赋值给this.elementData,也就是我们的数据存储区域。
有参构造方法
1.指定容量大小

​ 算法过程:

​ 判断initialCapaicity大小是否大于0,如果小于0就抛出异常,否则创建指定大小的数组并返回给this.elementData;

public ArrayList(int initialCapacity) {
        /**
         *如果列表的初始容量不为负,
         *那么就将数组缓冲区elementData重新初始化大小为initialCapacity
         *如果intialCapacity初始化为0,那么将用于空实例的共享空数组实例引用赋值给它。
         *如果initialCapacity为负,那么抛出异常。
         */
        if (initialCapacity > 0) { 
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
}
2.指定元素列表的构造方法。

​ 算法步骤:

​ 1.获取到元素列表的数组副本。

​ 2.如果大小等于等于0,就给elementData赋值空数组。

​ 3.如果大小不等于0,继续判断。

​ 4.如果数据不是对象数组,那么将其复制一份封装为对象数组(Object[ ])。

/**
     * 构造一个包含指定Collection的元素的列表,这些元素是按照
     * 该collection的迭代器返回他们的顺序排列
     * @param c 将其元素放入此列表的集合
     * @throws NullPointerException 如果指定的集合为null 
     */
    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;
        }
    }

四、成员方法

1.trimTosize()

public void trimTosize();

如果实际数组的长度小于给定数组的空间,就开始判断,
如果数组的长度 == 0, 那么返回共享的空数组实例,
否则使用Arrays.copyOf方法返回一个新的且大小刚好为数组元素数量的数组。

 /**
     * 将此{@code ArrayList}实例的容量修改为
     * 列表的当前大小。应用程序可以使用此操作来最小化
     * 存储{@code ArrayList}实例。 
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

2.ensureCapacity()

public void ensureCapacity();

如果所设置的最小容量大于当前数组的长度,
且数组不为空,
且所设置的最小容量大于默认值10的话,
就增长数组的长度的容量。

/**
     * 如果有必要,增加此ArrayList实列的容量,
     * 以确保它至少能够容纳最小参数所指定的元素数量。
     * 保证容量
     * @param minCapacity //所需的最小容量
     */
    public void ensureCapacity(int minCapacity) {
        /**
         * 如果所设置的最小容量大于当前数组的长度,
         * 且数组不为空,
         * 且所设置的最小容量大于默认值10的话,
         * 就增长数组的长度的容量。
         */
        if (minCapacity > elementData.length && 
            !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA /*空数组 */ 
                && minCapacity <= DEFAULT_CAPACITY/*10*/)) {
            modCount++;
            grow(minCapacity);
        }
    }

3.grow(int minCapacity)

增长数组的长度。

调用newCapacity(minCapacity);函数来计算需要增长的长度。

	/**
     * 要分配的数组的最大大小(除非必要)。
     * 一些虚拟机在数组中保留一些标题字。
     * 尝试分配更大的数组可能会导致
     * OutOfMemoryError:请求的数组大小超出了VM限制
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 增加容量以确保它至少可以容纳
     * 最小容量参数指定的元素数。 
     * @param minCapacity 所需的最小容量
     * @throws OutOfMemoryError 如果minCapacity小于零抛出异常
     */
    private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

4.grow( )

调用grow(minCapacity)函数来增长长度。

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

5.newCapacity(int minCapacity)

/**
     * 返回至少等于给定最小容量的容量。
     * 如果足够则多增加,则返回增多50%的当前容量。
     * 
     * 步骤:
     * 1.先获取到当前数组的有效容量。
     * 2.计算出新的数组容量。
     * 3.如果新的数组容量 < 保证容量,那么返回保证容量。
     *   但是在此之前,需要先判断当前数组的安全性,
     *   如果是空数组,就返回max(默认容量,保证容量)
     *   如果保证容量 < 0,那么抛出异常
     * 4.如果新的数组容量大于保证容量,且小于MAX_ARRAY_SIZE,
     *   那么返回新的数组容量,否则返回超大容量。
     * @param minCapacity the desired minimum capacity
     * @throws OutOfMemoryError if minCapacity is less than zero
     */
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        //获取原来数组的长度
        int oldCapacity = elementData.length;
        //计算新的数组的长度,是原来数组长度的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果计算出来的新的数组长度 < 保证容量 
        if (newCapacity - minCapacity <= 0) {
            //如果当前数据缓冲区是空数组
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                //返回默认容量和保证容量的最大值
                return Math.max(DEFAULT_CAPACITY, minCapacity);
                //如果保证容量的最小值小于0,那么抛出异常
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

6.hugeCapacity(int minCapacity)

public static int hugeCapaCity(int minCapacity);

用于处理超大容量的函数,被newCapacity(int minCapacity)调用。

/**
     * 用于计算数组的超大容量
     * 触发前提:
     *      计算出来的新的容量 > MAX_ARRAY_SIZE,就使用保证容量来扩充获取超大容量
     */
    private static int hugeCapacity(int minCapacity) {
        //如果保证容量小于0,抛出异常
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        //如果保证容量大于 MAX_ARRAY_SIZE,就返回Interger.MAX_VALUE
        //否则返回数组最大容量
        return (minCapacity > MAX_ARRAY_SIZE)
            ? Integer.MAX_VALUE
            : MAX_ARRAY_SIZE;
    }

7.size()

public int size();

获取ArrayList对象的长度

 /**
     *返回此列表中的元素数
     *
     * @return 返回此列表中的元素数
     */
    public int size() {
        return size;
    }

8.isEmpty()

public boolean isEmpty();

获取数组是否为空。

通过判断数组长度是否为0;

 /**
     *如果此列表不包含任何元素,则返回{@code true}。
     *
     * @return 如果此列表不包含任何元素,则返回{@code true}。
     */
    public boolean isEmpty() {
        return size == 0;
    }

9.contains(Object o)

public boolean contains(Object o);

判断指定元素时候在列表中。

调用的是indexOf(Object o);

/**
     *
     * 如果此列表包含指定的元素,则返回{@code true}。
     * 更正式地说,仅当此列表包含以下内容时,返回{@code true}
     * 至少一个元素{@code e}这样
     * {@code Objects.equals(o,e)}。 
     * @param o 要检查其在此列表中是否存在的元素 
     * @return{@code true}如果此列表包含指定的元素 
     */
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

10.indexOf(Object o)

public int indexOf(Object o);

获取指定元素的下标值。

如果指定元素不存在,那么返回-1;

/**
     * 返回指定元素首次出现的索引
     * 在此列表中;如果此列表不包含元素,则为-1。
     * 更正式地,返回最低索引{@code i},这样
     * {@code Objects.equals(o,get(i))},
     * 或-1(如果没有这样的索引)。 
     */
    public int indexOf(Object o) {
        /**
         * 如果传入数据位空指针,那么将找到第一个为空的位置返回,
         * 如果不为空,那么找到指定元素的位置返回,
         * 如果列表中不包含该对象那么返回-1。
         */
        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;
    }

11.lastIndexOf( Object o)

public int lastIndexOf(Object o);

返回指定元素最后一次出现的索引值。

/**
     * 返回指定元素最后一次出现的索引
     * 在此列表中;如果此列表不包含元素,则为-1。
     * 更正式地,返回最高索引{@code i},这样
     * {@code Objects.equals(o,get(i))},
     * 或-1(如果没有这样的索引)。
     */ 
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

12.clone()

public Object clone();
 /**
     *返回此{@code ArrayList}实例的浅表副本。 (
     *元素本身不会被复制。)
     *
     *@返回此{@code ArrayList}实例的副本
     */ 
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

13.toArray()

public Object[] toArray()

返回一个包含此对象中所有元素的数组。

/**
     * 返回一个包含此列表中所有元素的数组
     * 按正确的顺序(从第一个元素到最后一个元素)。
     *
     * <p>返回的数组将是“安全的”,因为没有对其的引用
     * 由该列表维护。 (换句话说,此方法必须分配
     * 一个新数组)。因此,调用者可以自由修改返回的数组。
     *
     * <p>此方法充当基于数组和基于集合的桥梁
     * API。
     *
     * @返回包含此列表中所有元素的数组
     * 正确的顺序
     */ 
    
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值