您的位置:首页 > 编程语言 > Java开发

Java源码阅读笔记-ArrayList

2018-03-30 14:01 435 查看
继承与实现
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList继承自AbstractList,实现了List,RandomAccess,Cloneable,Serializable。前两者都可以追溯到Collection接口,即要求实现size(),isEmpty()等集合类通用的方法。父类AbstractList事先实现了部分方法如indexOf(),clear(),还有一个很重要的ListIterator:这是一个专门用于List的遍历器,并且可以实现反向便利。后三个接口的作用为:1.RandomAccess标明ArrayList是支持随机访问的,可以使算法能够在随机和顺序访问的list中表现的更加高效。2.Cloneable标明了ArrayList是支持clone方法的,虽然protected clone()是Object类中定义的,但是如果尝试调用一个未实现Cloneable的方法,会抛出ClassNotSupportException。ArrayList的clone实现为{}大意为新建一个ArrayList,并调用工具类Arrays将核心数组拷贝给新ArrayList,并初始化修改计数器mobCount为0,最后返回。3.Serializable标明ArrayList可被序列化,后者实现了writeObject和readObject方法,主要是对自身数据进行for循环遍历逐个转化。
构造器
    public ArrayList(int initialCapacity) {}
首当其冲的是带参构造器public ArrayList(int i) {} 通过参数预设好了内部数组elementData的长度。此构造器适合在实现知道数据集的长度或者最大长度时调用,当然这并不意味着数组的长度被定死了。详情稍后解析。
public ArrayList(){}
第二位是无参构造器public ArrayList() {}直接讲elementData赋值为默认长度数组(10位)。    public ArrayList(Collection<? extends E> c) {}第三位构造器以集合为构造器,要求集合元素与自身所存元素对应,实现逻辑为:直接调用数据源的toArray()获取存储数据的数组并赋值给elementData,并做了检验,当toArray()返回的不是对应类型数组时,重新调用Arrays.copyOf进行拷贝。
如果ArrayList本质是用数组来存储数据的,那么如何保证能无限的存储数据而不发生溢出indexoutofboundexception呢?原因就在elementData数组并非一成不变,当ArrayList发现当前elementData已满时,便会进行一系列操作以拓展数组长度,核心在与grow(int minCapacity){}基本逻辑就是计算出一个合适的新范围newCapacity,然后将数据转移到一个新数组并赋值给elementData,新数组的长度自然就是newCapacity。程序逻辑复杂之处并不在拓展而在于计算新范围,默认拓展为原来的三倍,当要求拓展的范围更大时使用指定的范围,这样不可避免会出现数组的长度比int的最大值还要大,所以对此特殊情况也会特殊处理。数组拓展的操作并非ArrayList内部专享,ArrayList还提供了一个public void ensureCapacity(int minCapacity){}供外部使用者调用以拓展。
一系列的ArrayList状态量:size、isEmpty、contains、indexOf、lastIndexOf都是非常简单的实现,值得注意的是indexOf是用for循环逐个用equals进行比较而不是使用==,这就意味一旦equals匹配成功,即使地址不同也会返回对应下标。
一系列操作方法get、set、add、remove等get、set在操作之前都要对index进行检查,逻辑很好理解。add方法在添加元素之前先对当前容量进行检查,而进行插入操作的add(int index,E element)使用了一个System类的静态方法arrayCopy将elementData空出一个位置,再插入数据。addAll方法用于直接添加一整个数据集,ArrayList会尝试拓展一下长度,再去拷贝数据。两个remove方法,一个移除对应下标的元素,另一个移除指定的对象。前者自己实现了自己的移除逻辑,后者调用了内部方法fastRemove()进行移除,问题在于两者逻辑都是一模一样的,代码都没变过一个字,为什么在这种地方会出现重复代码,实在是令人困惑。removeAll方法用于批量删除数据,但要求参数中的数据集不能包含Null元素。再调用batchRemove方法,核心逻辑为遍历,保留需要留下的元素,在finally代码块中对尾部数据进行了剔除,返回的boolean量表示是否有元素发生了改变,不一定代表是否操作完全成功。removeIf方法是java1.8新增的,用于函数式编程,需要注意的是结果返回的true并不代表完全操作成功,而是指是否有任何一个元素发生了更改。同理的还有r
4000
eplaceAllclear方法直接遍历所有元素并设为null,交给GC回收。sort方法,使用传入的比较器进行排序,由于排序为耗时操作而ArrayList又不是线程安全的,有可能在排序时发生元素的更改,所以在排序完成后检查一下期间是否发生了更改,一旦更改直接报错
内部类Itr和ListItr这两个都是ArrayList交给使用者的遍历器,区别在于Ltr实现了向后遍历next()和删除当前遍历到的元素的remove()方法,还有一个可用于函数式编程的批量操作函数forEachRemaining。而ListItr在继承Ltr之后额外实现了向前遍历previous,修改set,添加add方法,可以更加方便地操作ArrayList。
内部类subList这是一个类似于视图的存在,相当于截取一段数据供操作,如果对父list进行了结构性修改(对长度进行了改变)将会直接导致当前subList失效,而两者的非结构性操作都会影响彼此。令人吃惊的是,JDK甚至在subList内部专门实现了一个ListIterator供使用,基本逻辑与ArrayList的遍历器无差,只是对实际操作的数据下标进行了适配。
内部类Spliterator这是java1.8新增的并行遍历迭代器,对其独立进行分析又可以独立写一篇笔记了,就先略tou过lan。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java源码 ArrayList