JAVA语言之详解 ArrayList
从安 2019-06-11 来源 : 阅读 1142 评论 0

摘要:本篇文章主要讲述JAVA语言之详解 ArrayList,希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入。

本篇文章主要讲述JAVA语言之详解 ArrayList,希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入。

JAVA语言之详解 ArrayList

一、基础属性

1.1 内部参数

 

    //空存储实例。直接new ArrayList()便是以该空数组作为实例

    private static final Object[] EMPTY_ELEMENTDATA = {};

 

    //默认容量大小,在由空实例进行首次扩容时,扩到到该长度。

    //实际使用中,并未实际存在Capacity这个参数,需要扩容时直接根据旧数组的length进行扩容

    private static final int DEFAULT_CAPACITY = 10;

 

    //实际存储数组

    transient Object[] elementData;

 

    //存储元素个数

    private int size;

 

    //该字段用于迭代器的fail-fast机制【参考另一篇文章】

    protected transient int modCount = 0;

1.2 三个重载构造方法

    //构建一个空实例,elementData指向容量为0的空数组

    public ArrayList() {

        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

    }

 

    //以给定容量初始化存储数组

    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);

        }

    }

 

    //以集合初始化创建列表

    //步骤:1. 调用toArray(),将给定集合转成数组(以ArrayList为例,toArray()返回的是实际存在元素的那部分数组,即[0,size))

    //2. 让size直接等于给定集合的数组长度

    //3. 判断size如果为0则直接创建空存储实例,否则使用Arrays.copyOf将给定集合数组复制一份(浅拷贝),作为存储数组

    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;

        }

    }

 

    // ArrayList 的 toArray()源码,复制[0,size)部分返回

    public Object[] toArray() {

        return Arrays.copyOf(elementData, size);

    }

二、操作及策略

2.1 动态扩容

扩容策略:当数组全满了才扩容,新长度=旧长度*1.5

动态扩容有两个入口:供用户调用的显式扩容ensureCapacity()和添加元素时的隐式扩容ensureCapacityInternal(),不过均是调用ensureExplicitCapacity()来根据传入所需容量值决定是否扩容,最终实际扩容操作在grow()方法中。

    //显式扩容入口方法

    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);

        }

    }

 

    //隐式扩容入口方法

    //其中参数 minCapacity 值为 size+1,由add方法调用传入

    private void ensureCapacityInternal(int minCapacity) {

        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

    }

 

    //添加方法

    public boolean add(E e) {

        // 传入最小所需容量为size+1

        ensureCapacityInternal(size + 1);  // Increments modCount!!

        elementData[size++] = e;

        return true;

    }

 

    //计算所需容量,实则不需要计算

    //只是单纯判断当前是否是空实例,为空就话返回 "默认容量"与minCapacity之间的较大值,不为空直接返回minCapacity

    //参数 minCapacity 只有两种情况:

    // 1. 隐式扩容时,如add()传入,其值为 size+1

    // 2. 显式扩容,用户指定

    private static int calculateCapacity(Object[] elementData, int minCapacity) {

        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

            return Math.max(DEFAULT_CAPACITY, minCapacity);

        }

        return minCapacity;

    }

    

    //在此处,根据最小所需容量来判断是否实际进行扩容

    private void ensureExplicitCapacity(int minCapacity) {

        modCount++;

 

        // overflow-conscious code

        if (minCapacity - elementData.length > 0) //若已经占满,才进行扩容

            grow(minCapacity);

    }

 

    //实际扩容方法

    //可以看到扩容策略为:length + length*0.5(右移1位缩小1倍)

    //然后调用Arrays.copyOf浅复制

    private void grow(int minCapacity) {

        // overflow-conscious code

        int oldCapacity = elementData.length;

        int newCapacity = oldCapacity + (oldCapacity >> 1); //旧长度+旧长度*2

        if (newCapacity - minCapacity < 0)

            newCapacity = minCapacity;

        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);

    }

2.2 删除操作

ArrayList中有两个remove方法public E remove(int index)和public boolean remove(Object o)

源码实现得其实挺简单,本来还在犹豫有没有必要把remove方法记录下来,但发现有个点值得注意以下:
注意点:JDK对于数组内元素统一移动操作,偏好使用System.arraycopy(ary, srcPos, ary, destPos, len)来移动

 

    //通过给定下标来删除元素,并返回删除元素

    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); // 使用System.arraycopyl来移动数组元素

        elementData[--size] = null; // clear to let GC do its work  // 删除后size-1,并将先前最后一个元素置null

 

        return oldValue; //返回删除元素

    }

 

    // 判断下标是否合理(<size),否则抛出异常

    private void rangeCheck(int index) {

        if (index >= size)

            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    }

 

    // 删除首个与给定元素相等的元素,元素判等使用equal方法

    // 成功返回true,失败返回false

    public boolean remove(Object o) {

        if (o == null) { // 可以删除null节点

            for (int index = 0; index < size; index++)

                if (elementData[index] == null) {

                    fastRemove(index); // 参考下面方法注释

                    return true;

                }

        } else {

            for (int index = 0; index < size; index++)

                if (o.equals(elementData[index])) { // 从头开始一个个检查,使用equal判等

                    fastRemove(index);

                    return true;

                }

        }

        return false;

    }

 

    /*

     * 官方注释说得很清楚了:删除元素,但跳过边界检查且不返回删除元素。(就是remove(index)的缩略版

     * Private remove method that skips bounds checking and does not

     * return the value removed.

     */

    private void fastRemove(int index) {

        modCount++;

        int numMoved = size - index - 1;

        if (numMoved > 0)

            System.arraycopy(elementData, index+1, elementData, index,

                             numMoved);

        elementData[--size] = null; // clear to let GC do its work

    }


本文由职坐标整理发布,学习更多的相关知识,请关注职坐标IT知识库!

 

本文由 @从安 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 1
看完这篇文章有何感觉?已经有1人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved