父类和接口
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
1、java.util.AbstractList
该抽象类是大部分 List 的共同父类,它提供了一些基本的方法封装,以及通用的迭代器实现。
2、java.util.List
列表标准接口,列表是一个有序集合,又被成为序列。该接口对它内部的每一个元素的插入位置都有精确的控制。
可以使用整数索引(index)来查询。
列表允许重复元素,可以插入 null 元素
3、java.util.RandomAccess
这是一个标记性质的随机访问接口,它没有提供任何方法。
如果实现了这个接口,那么标识这个类使用索引遍历比迭代器要更快(ArrayList、CopyOnWriteArrayList、Stack 和 Vector 都实现了这个接口)
4、java.long.Cloneable
用于标记可克隆的对象。没有实现该接口的对象在调用 Object.clone() 方法时会抛异常。
5、java.io.Serializable
序列化标记接口。被此接口标记的类可以实现 Java 序列化和反序列化。该接口没有任何内容,但是 Java 序列化里有一些默认成员变量和默认方法,会在序列化和反序列化的时候调用,主要有以下方法:
/* 序列化时调用,可以将特定的类转换成自己需要的序列化格式 */
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException
/* 反序列化时调用,可以将输入转换成特定的类*/
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException
/* 反序列化时调用,当遇到类似版本不一致的问题,为了使序列化成功,提供的一个缺省的方法 */
private void readObjectNoData() throws ObjectStreamException
成员变量
1、transient Object[] elementData;
elementData 是该 List 的数据域,其中被 transient 修饰表示这个变量不会被序列化,它提供给 Serializable 接口使用。
但是 ArrayList 的序列化和反序列化都能成功,是因为实现了 Serializable 接口的 writeObject 方法,这个方法把 elementData 中的元素全部序列化到文件中了。writeObject 是私有方法,通过反射机制被调用。
为何不直接使用 elementData 来序列化?**
因为 elementData 是一个缓存数组,为了性能考虑,通常会预留一些容量,当容量不足时会扩容,可能会有大量空间没有实际存储元素。通过实现 writeObject 方法来保证只序列化实际有值的元素,而不是序列化整个数组。
2、private int size;
size 表示当前 List 的长度。elementData 的 length 是必然大于或等于 size 的。
3、protected transient int modCount = 0;
该成员变量继承自 java.util.AbstractList,记录了结构性变化的次数。涉及结构性变化的方法都会增加 modCount 的值,
这些方法包括:add()、remove()、addAll()、removeRange()、clear()。
常量
1、private static final long serialVersionUID = 8683452581122892189L;
序列化版本 UID,供序列化接口使用,维持版本一致性
设想 ArrayList 在某次更新后,多出了新成员要被序列化,那么旧版本中序列化的内容就无法序列化成新版本的对象。
2、private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
设想数组长度的上限,最大整数 - 8
构造方法
ArrayList 有三个重载的构造方法
public ArrayList(int initialCapacity)
public ArrayList()
public ArrayList(Collection<? extends E> c)
initialCapacity 表示初始化的 elementData 长度;
如果使用无参构造,elementData 初始长度为0,add 第一个元素时默认初始长度为 10;
当构造参数为集合时,elementData 长度会设置等同集合的大小,然后赋值所有元素到 elementData 中。
常用方法
1、indexof、lastIndexof、contains
- indexof :用于查询指定对象的索引 index,实现的方式是对数组顺序遍历,调用指定元素的 equals 方法来比对。如果查询不到,返回 -1。
- lastIndexof:与 indexof 遍历顺序相反,对数组倒序遍历查找。
- contains:直接调用 indexof 方法,根据返回值是否是 -1 来判断查找的元素是否存在。
2、set、add、addAll
- set:替换数组里的对应索引处的值
- add 和 addAll :首先要检查当前 elementData 的长度,如果添加后的大小超出 elementData 的长度,需要对 elementData 扩容。
- 扩容:按照1.5倍的比例扩容。
3、remove、removeAll、retainAll
- remove:有两种重载形式:remove(int) 和 remove(Object)
- 当形参为 int 时表示移除位于指定 index 的数据,如果移除的不是最后一位,会调用 System.arrayCopy 方法把 index 之后的数据向前移动一位,该方法的返回值指向被删除的元素。由此可见 ArrayList 的 remove 方法效率比较低。
- 当形参为 Object 时,表示移除指定的对象,该方法会遍历整个数组,找到第一个与之相等的对象,并执行类似 remove(int) 的操作。返回是否删除成功。
- public boolean removeAll(Collection<?> c)
- 从列表中移除指定集合中包含的所有元素
- public boolean retainAll(Collection<?> c)