20200817
关于List接口中的ArrayList
ArrayList是实现List接口的最成见的一种方式,与数组有类似的性质
- 和顺序访问相比随机访问效率高
- 读快写慢,是因为ArrayList的数据结构导致的(写操作会涉及到数据的移动)
ArrayList的父类和实现的接口
1. java.util.AbstractList
此抽象类是大部分List的共同父类,继承于java.util.AbstractCollection,AbstractList类提供了基本的操作方法(add, get, set, remove, indexOf, lastIndexOf…)以及通用的迭代器的实现
2. java.util.List
列表的标准接口,是有序集合,故称序列,可存入相同元素和null值
3. java.util.RandomAccess
该接口没有提供任何方法,只作为标记使用,如果一个类实现了这个接口,意味着这个类使用所以遍历会比迭代器遍历要快(事实证明确实是索引遍历更快一些,使用System.currentTimeMillis()进行做差对比得出结论)
4. java.lang.Cloneable
此接口的意义就是标志着ArrayList类对象可以使用Clone()方法,如果没实现此接口的类的对象调用clone()方法会抛出异常
5. java.io.Serializable
此接口为序列化标记接口,实现此接口意味该类可以实现Java的序列化和反序列化,该接口本身没有提供任何方法,但是Java序列化里有些默认成员变量和默认方法会在序列化和反序列化操作的时候调用到:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
private void readObjectNoData() throws ObjectStreamException
(这三个方法下期出一下源码解析)
ArratList的三个成员变量和两个常量
成员变量
1. transient Object[] elementData
elementData是ArrayList的数据域,transient修饰符表示这个变量不会被序列化,提供给Serializable接口使用,但是实际操作会发现ArrayList的序列化和反序列化操作都成功了,其实是调用了ArrayList内部的另一个方法
/**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The length of the array backing the <tt>ArrayList</tt>
* instance is emitted (int), followed by all of its elements
* (each an <tt>Object</tt>) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
这个方法是通过反射机制调用的
为什么不用elementData序列化而调用此方法呢,是因为elementData是一个缓存数据,为了性能考虑,elementData数据会预留一些容量(频繁的扩容会消耗大量的资源这么做不划算),调用writeObject(java.io.ObjectOutputStream s) 方法实现序列化,只会序列化那些有实际意义的数据,而不是序列化整个数组
2. private int size
size表示当前List的长度,值得注意的是elementData数组的length是必大于size的,一方面是因为数组后面可能会有一些没有实际意义不计入长度的的null元素,另一方面就是频繁的扩容会消耗大量的资源,对性能方面来讲这就是不必要的;ArrayList提供了一系列的机制来维持elementData的大小,并且提供了size来表示当前数组的大小
3. protected transient int modCount = 0;
该成员继承于java.util.AbstractList,记录ArrayList的结构性变化次数,只要ArrayList发生了结构性变化,modCount都会 +1,方法包括add(), remove(), addAll(), removeRange(), clear()
常量
1. private static final long serialVersionUID = 8683452581122892189L
此常量为ArrayList的序列化ID
2. private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
定义了数组的最大长度为Integer的最大值 - 8
构造方法
ArrayList有三个重载的构造方法
public ArrayList(int initialCapacity)
public ArrayList()
public ArrayList(Collection<? extends E> c)
initialCapacity表示初始化的时候设定elementData数组的长度
如果传入参数为集合,则先把elementData的长度设置为和集合相等的大小,然后复制集合里的所有元素到ArrayList的elementData数组中