博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JDK1.8 ArrayList部分源码分析小记
阅读量:6719 次
发布时间:2019-06-25

本文共 7905 字,大约阅读时间需要 26 分钟。

JDK1.8 ArrayList部分源码分析小记

底层数据结构

底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。

我们对ArrayList类的实例的所有的操作底层都是基于数组的。

继承与实现关系

ArrayList继承的父类为:AbstractList(抽象类)

实现的接口有:List(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)

友情提示:因为ArrayList实现了RandomAccess接口,所以尽量用for(int i = 0; i < size; i++) 来遍历而不要用Iterator迭代器来遍历,后者在效率上要差一些

public class ArrayList
extends AbstractList
implements List
, RandomAccess, Cloneable, java.io.Serializable

部分属性

类的属性中核心的属性为elementData,类型为Object[],用于存放实际元素,并且被标记为transient,也就意味着在序列化的时候,此字段是不会被序列化的。

友情提示:ArrayList的默认容量为10

// 缺省容量    private static final int DEFAULT_CAPACITY = 10;    // 元素数组(调用指定初始值的构造函数时elementData的长度会变成指定值)    transient Object[] elementData;     // 空对象数组    private static final Object[] EMPTY_ELEMENTDATA = {};    // 缺省空对象数组    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

常用构造函数

友情提示:创建ArrayList时尽量设置初始大小(使用ArrayList(int initialCapacity)构造函数)

/**     * ArrayList带容量大小的构造函数     *     * @param initialCapacity     */    //说明:指定elementData数组的大小,不允许初始化大小小于0,否则抛出异常。    public ArrayList(int initialCapacity) {        if (initialCapacity > 0) {// 初始容量大于0            this.elementData = new Object[initialCapacity];// 初始化元素数组(新建一个数组)        } else if (initialCapacity == 0) {// 初始容量为0            this.elementData = EMPTY_ELEMENTDATA;// 为空对象数组        } else {// 初始容量小于0,抛出异常            throw new IllegalArgumentException("Illegal Capacity: " +                    initialCapacity);        }    }        /**     * ArrayList无参构造函数。默认容量是10     */    //说明:当未指定初始化大小时,会给elementData赋值为空集合。    public ArrayList() {        // 无参构造函数,设置元素数组为空        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;    }

常用函数分析

友情提示

add方法:当容量到达size时进行扩容(add(E e)中先调用了ensureCapacity(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增),扩容为当前容量的0.5倍(若果ArrayList的当前容量为10,那么一次扩容后的容量为15)
set方法:调用set方法时会检验索引是否合法(只校验了上限)(index不能等于size(index<size))
get方法:调用get方法时也会检验索引是否合法(只校验了上限)(index不能等于size(index<size))
remove方法:在移除指定下标的元素时,会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后整个数组不被使用时,可以被GC;元素移动时使用的是System.arraycopy()方法

/**     * 添加元素     *     * @param e     * @return     */    public boolean add(E e) {        //确保elementData数组有合适的大小        ensureCapacityInternal(size + 1);         elementData[size++] = e;        return true;    }    /**     * 设定指定下标索引的元素值     *     * @param index     * @param element     * @return     */    public E set(int index, E element) {        // 检验索引是否合法        rangeCheck(index);        // 旧值        E oldValue = elementData(index);        // 赋新值        elementData[index] = element;        // 返回旧值        return oldValue;    }    /**     * 获取指定下标的元素     *     * @param index     * @return     */    //说明:get函数会检查索引值是否合法(只检查是否大于size,而没有检查是否小于0),    // 值得注意的是,在get函数中存在element函数,element函数用于返回具体的元素     public E get(int index) {        // 检验索引是否合法        rangeCheck(index);        return elementData(index);    }    // Positional Access Operations    /**     * 位置访问操作     *     * @param index     * @return     */    //说明:返回的值都经过了向下转型(Object -> E(泛型))    @SuppressWarnings("unchecked")    E elementData(int index) {        return (E) elementData[index];    }    /**     * 移除指定下标元素     *     * @param index     * @return     */    //说明:remove函数用户移除指定下标的元素    // 此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,可以会被GC。    //提醒:元素移动时使用的是System.arraycopy()方法    public E remove(int index) {        // 检查索引是否合法        rangeCheck(index);        modCount++;        E oldValue = elementData(index);        // 需要移动的元素的个数        int numMoved = size - index - 1;        if (numMoved > 0)            System.arraycopy(elementData, index + 1, elementData, index,                    numMoved);        // 赋值为空,有利于进行GC        elementData[--size] = null; // clear to let GC do its work        // 返回旧值        return oldValue;    }    /**     * 在指定下标位置插入元素     * @param index     * @param element     */    public void add(int index, E element) {        //检查索引是否合法        rangeCheckForAdd(index);        ensureCapacityInternal(size + 1);         System.arraycopy(elementData, index, elementData, index + 1,                size - index);        elementData[index] = element;        size++;    }

其他方法介绍

友情提示:rangeCheckForAdd方法用于add(int index, E element)和addAll(int index, Collection<? extends E> c)方法中检验索引是否合法;rangeCheck方法用于get、set等方法中检验索引是否合法(因为不改变数据结构,故index不能取到size,最大只能取到size-1)

//检验索引是否合法(只校验了上限)(index不能等于size(index
= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //检验索引是否合法(校验了上限和下限)(index可以等于size(0<=index<=size)) private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }

扩容策略

// 确定ArrarList的容量。    // 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2 + 1”    public void ensureCapacity(int minCapacity) {        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)                ? 0                : DEFAULT_CAPACITY;//默认容量10        if (minCapacity > minExpand) {            ensureExplicitCapacity(minCapacity);        }    }    //确保elementData数组有合适的大小    private void ensureCapacityInternal(int minCapacity) {        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 判断元素数组是否为空数组            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);// 取较大值        }        //确保elemenData数组有合适的大小        ensureExplicitCapacity(minCapacity);    }    //确保elemenData数组有合适的大小    private void ensureExplicitCapacity(int minCapacity) {        // 结构性修改加1        modCount++;        // overflow-conscious code        if (minCapacity - elementData.length > 0)            grow(minCapacity);    }        //对数组进行扩容    private void grow(int minCapacity) {        // overflow-conscious code        int oldCapacity = elementData.length;// 旧容量        int newCapacity = oldCapacity + (oldCapacity >> 1);// 新容量为旧容量的1.5倍        if (newCapacity - minCapacity < 0)// 新容量小于参数指定容量,修改新容量            newCapacity = minCapacity;        if (newCapacity - MAX_ARRAY_SIZE > 0)// 新容量大于最大容量            newCapacity = hugeCapacity(minCapacity);// 指定新容量        // 拷贝扩容        elementData = Arrays.copyOf(elementData, newCapacity);    }

部分方法测试

  • add方法
public class AddTest {    @Test    public void test1(){        //我们可以看到,在add方法之前开始elementData = {};        // 调用add方法时会继续调用,直至grow,最后elementData的大小变为10,        // 之后再返回到add函数,把8放在elementData[0]中        List
lists = new ArrayList
(); lists.add(8); } @Test public void test2(){ //说明:我们可以知道,在调用add方法之前,elementData的大小已经为6,之后再进行传递,不会进行扩容处理。 List
lists = new ArrayList
(6);//elementData.length=6 lists.add(8); } }
  • rangeCheck方法
public class RangeCheckTest {    @Test    public void test() {        List list = new ArrayList();        list.add(1);        list.add(2);        list.add(3);        //该语句报ArrayIndexOutOfBoundsException异常是rangeCheck(index)引发的(index >= size)        System.out.println(list.get(10));        //rangeCheck(index)方法只校验上线,该语句报ArrayIndexOutOfBoundsException异常是elementData[index]引发的        System.out.println(list.get(-1));        list.remove(-1);//同上        Object[] a = new Object[]{1, 2, 3};        System.out.println(a[-1]);    }}
  • indexOf方法
public class IndexOfTest {    @Test    public void test(){        List list = new ArrayList();        list.add(null);        list.add(2);        list.add(2);        list.add(null);        System.out.println(list.indexOf(null));//0        System.out.println(list.indexOf(2));//1        System.out.println(list.indexOf(3));//-1    }}
  • toArray方法
public class ToArrayTest {    @Test    public void test() {        List list = new ArrayList(10);        list.add(1);        list.add(2);        list.add(3);        list.add(4);        Object[] array =list.toArray();        //调用Arrays.copyOf()-->调用System.arraycopy()    }}

转载地址:http://lcjmo.baihongyu.com/

你可能感兴趣的文章
linux-sed
查看>>
16.4-16.8 Tomcat监听80端口,Tomcat的虚拟主机,访问日志
查看>>
app客户端测试
查看>>
nodejs渐入佳境[23]-hash函数
查看>>
Big Data Integration with Hadoop: A Q&A Spotlig...
查看>>
【062有新题】OCP 12c 062出现大量之前没有的新考题-16
查看>>
触手TV下载|触手TVapp下载
查看>>
PDF文件如何修改,PDF怎么添加文本高亮
查看>>
大链表数据去重的办法
查看>>
Awk使用案例总结(运维必会)
查看>>
卸载并清理gitlab
查看>>
Nginx 负载均生产环境下的衡配置
查看>>
关于流量计算
查看>>
python笔记-循环
查看>>
未来技术与安全
查看>>
2012中国虚拟化及云计算技术年度市场研究报告
查看>>
进程管理
查看>>
Kali 开机自动启动服务
查看>>
我的友情链接
查看>>
SQL(三)、SQL语句练习
查看>>