ArrayList解析
ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
-
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
-
ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
-
ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
-
ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
-
和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
ArrayList包含了两个重要的对象:elementData 和 size。
-
elementData 是"Object[]类型的数组",它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,请参考源码分析中的ensureCapacity()函数。
-
size 则是动态数组的实际大小。
关键字transient
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。
被标记为transient的属性在对象被序列化的时候不会被保存,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
快速失败(fail-fast)
fail-fast 机制是Java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出
ConcurrentModificationException异常,产生fail-fast事件。
要了解fail-fast机制,我们首先要对ConcurrentModificationException 异常有所了解。当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常。同时需要注意的是,该异常不会始终指出对象已经由不同线程并发修改,如果单线程违反了规则,同样也有可能会抛出改异常。
诚然,迭代器的快速失败行为无法得到保证,它不能保证一定会出现该错误,但是快速失败操作会尽最大努力抛出ConcurrentModificationException异常,所以因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。
ArrayList中允许元素为null,因为我们会在后面的源码解析中看到查找给定元素索引值等的方法中,源码都将该元素的值分为null和不为null两种情况处理。
扩容操作
扩容结束后,调用了Arrays.copyOf(elementData,newCapacity)方法,这个方法中先创建了一个新的容量为newCapacity的对象数组,然后使用System.arraycopy()方法将旧的对象数组复制到新的对象数组中。新数组大小为原来的1.5倍,arraycopy 为System类中的native方法,有比较大的性能开销,所以在我们使用ArrayList时,最好能预计数据的数量并在第一次创建时就申请够内存,否则建议使用LinkedList。
int newCapacity = oldCapacity + (oldCapacity >> 1);