JDK中ArrayList的实现分析

JDK中ArrayList的实现分析

第一次使用Markdown写博客,使用MarkDownPad 2先写好拷贝过来的。

查看Android中的JDK源码ArrayList的实现.

JDK中的List类族,参考《Java程序性能优化》3.2.1节:

这里写图片描述

ArrayList继承自AbstractList,后者是一个抽象类,又继承自AbstractCollection,并实现了List接口。

public abstract class AbstractList<Eextends AbstractCollection<Eimplements List<E>

查看源码注释:

AbstractList is an abstract implementation of the List interface, optimized
for a backing store which supports random access. This implementation does
not support adding or replacing. A subclass must implement the abstract
methods get() and size(), and to create a
modifiable List it’s necessary to override the add() method that
currently throws an UnsupportedOperationException.

可以知道,AbstractList支持随机访问,但是并不支持添加和替换,它的子类必须实现get()和size()这两个抽象方法来创建一个可以修改的List。另外要复写add()方法,因为在AbstractList中,该方法仅仅是抛出了一个UnsupportedOperationException异常。

public void add(int location, E object) {  
   throw new UnsupportedOperationException();  
}  

AbstractList里面定义了两个内部类SimpleListIterator和FullListIterator,前者可以从List第一个元素开始遍历,而后者继承自前者,可以实现从指定位置开始遍历,AbstractList里面的访问都是通过这两个迭代器实现的。

回头再来看ArrayList的实现。
先看注释:

ArrayList is an implementation of List, backed by an array.
All optional operations including adding, removing, and replacing elements are supported.
All elements are permitted, including null.
This class is a good choice as your default List implementation.
Vector synchronizes all operations, but not necessarily in a way that’s meaningful to your application: synchronizing each call to get, for example, is not equivalent to synchronizing the list and iterating over it (which is probably what you intended).
java.util.concurrent.CopyOnWriteArrayList is intended for the special case of very high
concurrency, frequent traversals, and very rare mutations.

ArrayList是List的一个基于数组的实现。它支持添加,删除以及替换原始这些所有的操作。包括null在内的所有元素都是允许的。
这个类是你实现List的一个好的选择。Vector类对所有操作进行了同步化,但是可能在你的程序中不是十分必要:比如同步化每一次get的调用,不等于同步化整个list以及它的遍历(这才可能是你希望的)。
java.util.concurrent.CopyOnWriteArrayList用在高并发,经常遍历以及很少变化的情况下。

可以知道Vector和CopyOnWriteArrayList是两个线程安全的List的实现,而ArrayList不是线程安全的。并发List的讲解可以参考Java程序性能优化一书的4.3节“JDK并发数据结构”中的“并发List”小节。

又跑题了。
ArrayList支持随机访问,并使用数组实现,其中定义一个容量增长的最小值:

private static final int MIN_CAPACITY_INCREMENT = 12;

这个值是时间和空间上的一个权衡。这就表示虽然基于数组实现,但是容量并不一定要指定,必要时候容量可以自动扩充。

毫无疑问,类里面会定义一个数组:

transient Object[] array;

这个数组用于存储元素,元素值当然可以为null,另外也支持泛型,可以存储不同类型的元素。

如果我们指定了构造函数中的容量,array就会根据容量进行初始化,当然如果容量小于0就会抛出IllegalArgumentException异常:

array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);

EmptyArray.OBJECT是定义的一个容量为0的对象。

ArrarList还实现了其他的构造函数:

public ArrayList(Collection<? extends E> collection) 

该函数可以从一个集合中进行初始化,拷贝它的元素。

我们在ArrayList中添加元素的时候,调用add()方法,下面看看是怎么实现的。

@Override public boolean add(E object) {
    Object[] a = array;
    int s = size;
    if (s == a.length) {
        Object[] newArray = new Object[s +
                (s < (MIN_CAPACITY_INCREMENT / 2) ?
                 MIN_CAPACITY_INCREMENT : s >> 1)];
        System.arraycopy(a, 0, newArray, 0, s);
        array = a = newArray;
    }
    a[s] = object;
    size = s + 1;
    modCount++;
    return true;
}

当要添加元素object的时候,首先定义array的一个引用,判断一下元素个数是否等于数组的长度,即元素是否已经填满了数组,如果填满了,就没有多余的空间存储新的元素,所以此时要进行扩容。
扩容算法是这样的:
如果元素个数比MIN_CAPACITY_INCREMENT / 2小,就扩充MIN_CAPACITY_INCREMENT大小的空间,反之扩充元素个数一半的空间,用新的大小定义一个新的数组,将旧的数组的元素拷贝进去,然后将array的引用转移到新的数组,最后更新元素个数等信息。

如果要在指定位置添加元素,实现方式就不一样了。

@Override public void add(int index, E object) {
    Object[] a = array;
    int s = size;
    if (index > s || index < 0) {
        throwIndexOutOfBoundsException(index, s);
    }

    if (s < a.length) {
        System.arraycopy(a, index, a, index + 1, s - index);
    } else {
        // assert s == a.length;
        Object[] newArray = new Object[newCapacity(s)];
        System.arraycopy(a, 0, newArray, 0, index);
        System.arraycopy(a, index, newArray, index + 1, s - index);
        array = a = newArray;
    }
    a[index] = object;
    size = s + 1;
    modCount++;
}

首先判断位置是否超出边界,超出就抛出throwIndexOutOfBoundsException异常。然后如果数组空间足够,将插入位置以后的元素整体向后挪动一个单位,否则先用前面的算法扩容,然后整体挪动。
数组整体搬移一个单位的操作还是比较消耗性能的,尤其是大量存在插入操作的时候,这个时候可以考虑性能更好的LinkedListArray,它基于链表,我们下次再分析它的实现。

当要remove一个位置的元素时,源码是这样的:

@Override public E remove(int index) {
    Object[] a = array;
    int s = size;
    if (index >= s) {
        throwIndexOutOfBoundsException(index, s);
    }
    @SuppressWarnings("unchecked") E result = (E) a[index];
    System.arraycopy(a, index + 1, a, index, --s - index);
    a[s] = null;  // Prevent memory leak
    size = s;
    modCount++;
    return result;
}

很简单,直接将指定位置元素的数组整体前移一个单位,这样就将要删除的元素覆盖掉了。

如果要remove某个对象元素时,remove(Object object)函数需要先判断object是否为null,再分为两种情况处理。如果不是null,就寻找满足object.equals(a[i])的位置,进行整体前移的操作;如果为null,则直接查找第一个为a[i] == null的地方,整体前移。

ArrayList其他的操作包括contains()、indexOf()等都比较好理解,就是简单的数组遍历和比较,就不再赘述了。

到此为止,我们基本上了解了ArrayList的实现。

相关推荐
<p> 欢迎参加英特尔® OpenVINO™工具套件初级课程 !本课程面向零基础学员,将从AI的基本概念开始,介绍人工智能与视觉应用的相关知识,并且帮助您快速理解英特尔® OpenVINO™工具套件的基本概念以及应用场景。整个课程包含了视频的处理,深度学习的相关知识,人工智能应用的推理加速,以及英特尔® OpenVINO™工具套件的Demo演示。通过本课程的学习,将帮助您快速上手计算机视觉的基本知识和英特尔® OpenVINO™ 工具套件的相关概念。 </p> <p> 为保证您顺利收听课程参与测试获取证书,还请您于<strong>电脑端</strong>进行课程收听学习! </p> <p> 为了便于您更好的学习本次课程,推荐您免费<strong>下载英特尔® OpenVINO™工具套件</strong>,下载地址:https://t.csdnimg.cn/yOf5 </p> <p> 收听课程并完成章节测试,可获得本课程<strong>专属定制证书</strong>,还可参与<strong>福利抽奖</strong>,活动详情:https://bss.csdn.net/m/topic/intel_openvino </p> <p> 8月1日-9月30日,学习完成【初级课程】的小伙伴,可以<span style="color:#FF0000;"><strong>免费学习【级课程】</strong></span>,级课程免费学习优惠券将在学完初级课程后的7个工作日内发送至您的账户,您可以在:<a href="https://i.csdn.net/#/wallet/coupon">https://i.csdn.net/#/wallet/coupon</a>查询优惠券情况,请大家报名初级课程后尽快学习哦~ </p> <p> <span style="font-size:12px;">请注意:点击报名即表示您确认您已年满18周岁,并且同意CSDN基于商务需求收集并使用您的个人信息,用于注册OpenVINO™工具套件及其课程。CSDN和英特尔会为您定制最新的科学技术和行业信息,将通过邮件或者短信的形式推送给您,您也可以随时取消订阅不再从CSDN或Intel接收此类信息。 查看更多详细信息请点击CSDN“<a href="https://passport.csdn.net/service">用户服务协议</a>”,英特尔“<a href="https://www.intel.cn/content/www/cn/zh/privacy/intel-privacy-notice.html?_ga=2.83783126.1562103805.1560759984-1414337906.1552367839&elq_cid=1761146&erpm_id=7141654/privacy/us/en/">隐私声明</a>”和“<a href="https://www.intel.cn/content/www/cn/zh/legal/terms-of-use.html?_ga=2.84823001.1188745750.1560759986-1414337906.1552367839&elq_cid=1761146&erpm_id=7141654/privacy/us/en/">使用条款</a>”。</span> </p> <p> <br /> </p>
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页