概述
java.util.Collection是Java集合类的基本接口,存储和操作一组类型相同的对象。而ArrayList是java语言最常用的集合类,是表ADT的一种实现。
ArrayList继承树
实现
速览一下ArrayList的源码,详细介绍一下常用的方法。
属性
|
|
ArrayList中的数据存于一个数组Object[],可以理解为ArrayList对数组进行了一层封装,便于使用。
transient标记这个elementData不可序列化,而使用writeObject方法序列化ArrayList对象。
|
|
还有个继承于AbstractList的属性很重要,用于记录list结构修改的次数,更详细的说明后文中会有。
get&set
|
|
|
|
get和set方法都是对数组指定位置元素的操作。
add
|
|
add方法中需要先检查数组空间是否足够,如有必要则执行扩容,扩容使用Arrays.copyOf方法(底层调用System.arraycopy)
remove
|
|
remove方法实际也使用System.arraycopy
iterator
|
|
ArrayList有个内部类Itr实现了Iterator接口,提供hasNext(),next(),remove()等方法,使调用者在迭代collection时能做删除操作。
可以用iterator()方法返回一个迭代器,在使用foreach语法实际也用的是这个Itr
|
|
同理还有个ListIterator
fail-fast
之前提到ArrayList继承了AbstractList的一个非常重要的属性modCount,用于记录list结构改变的次数,在add(),remove()等代码中都有对modCount的自增操作。该属性用于iterator以及list iterator的实现,当modCount的值不符合预期,会抛异常ConcurrentModificationException。
以下代码就是典型的错误
|
|
然而,如果迭代器调用了它自己的remove方法,那么这个迭代器就仍然是合法的。
RandomAccess
发现ArrayList还实现了一个标记接口RandomAccess,文档中解释说这个接口的作用是使list的实现在随机访问时有更高的效率。
Spliterator
// TODO
java8新特性,有待研究,之后补上
小结
ArrayList提供了List ADT的一种可增长数组的实现,使用ArrayList的优点在于,对get和set调用花费常数时间。其缺点是新项的插入和现有项的删除代价昂贵,除非变动是在ArrayList的末端进行。
适合需要快速访问数据,较少插入或删除元素的场景。
变种
concurrent包下提供了CopyOnWriteArrayList
写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。
以下代码是向ArrayList里添加元素,可以发现在add方法中是需要加锁的
get方法则无需加锁
当线程A执行修改操作,修改的是copy出来的新数组,其它读线程此时读取的任是原数组。
那么问题来了,修改操作会锁住整个List,是一个并发瓶颈;频繁的copy则会导致内存和gc相关的问题。CopyOnWriteArrayList不是一个通用的并发List,适合读多写少的场景。