1.简介
ArrayList是我们开发中最常用的数据存储容器之一,其底层是数组实现的,我们可以在集合中存储任意类型的数据,ArrayList是线程不安全的,非常适用于对元素进行查找,效率非常高。
2.继承体系
ArrayList实现List、RandomAccess、Cloneable、Serializable等接口。
1.Arraylist实现List,提供了基础的添加、删除、遍历等操作。
2.ArrayList实现RandomAccess,提供随机访问的能力。
3.ArrayList实现Cloneable,可以被克隆。
4.ArrayList实现Serializable,可以被序列化。
3.线程安全性
对ArrayList的操作一般分为两个步骤,改变元素的位置(大小)和操作元素。这个过程在多线程的环境下是不能保证具有原子性的,因此ArrayList在多线程下是不安全的。
4.源码分析
/**
* Default initial capacity.
*
* 默认初始化容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*
* 如果自定义容量为0,则会默认用它来初始化ArrayList,或者用于空数组替换
*/
private static final Object[] EMPTY_ELEMENTDATA = {
};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*
* 如果没有自定义容量,则会使用它来初始化ArrayList,或者用于空数组比对。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*
* 这就是ArrayList底层用到的数组
* 非私有,用于简化嵌套类访问
* transient 在已经实现序列化的类中,不允许某变量序列化。
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*
* 实际ArrayList的大小/集合中元素的个数
*/
private int size;
拓展:什么是序列化?
序列化是指:将对象转换成以字节序列的形式来表示,以便于持久化和传输。
实现方法:实现序列化接口。
然后用的时候拿出来进行反序列化即可又变成java对象。
transient关键字解析
java中的transient关键字的作用,简单地说,就是让某些被修饰的变量不参加序列化。
有了transient
关键字声明,transient Object[] elementData
,事实上我们使用ArrayList在网络传输的时候很正常,并没有出现空值,为什么呢?
看源码:
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
// 防止序列化期间有修改
int expectedModCount = modCount;
// 写出非transient非static属性(会写出size属性)
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();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 声明为空数组
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
// 读入非transient非static属性(会读取size属性)
s.defaultReadObject();
// Read in capacity
// 读入元素个数,没什么用,只是因为写出的时候写了size属性,读的时候也要按顺序来读
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
// 计算容量
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
// 检查是否需要扩容
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
// 依次读取元素到数组中
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
查看writeObject()
方法可知,先调用s.defaultWriteObject()
方法,再把size写入到流中,再把元素一个一个的写入到流中。
一般地,只要实现了Serializable接口即可自动序列化,writeObject()
和readObject()
是为了自己控制序列化的方式,这两个方法必须声明为private,在java.io.ObjectStreamClass#getPrivateMethod()方法中通过反射获取到writeObject()这个方法。
在ArrayList的writeObject()
方法中先调用了s.defaultWriteObject()
方法,这个方法是写入非static非transient的属性,在ArrayList中也就是size属性。同样地,在readObject()
方法中先调用了s.defaultReadObject()
方法解析出了size属性。
那为什么不直接使用elementData中序列化,而采用上述的方式来实现序列化呢?
elementData
定义为transient
的优势,自己根据size序列化真实的元素,而不是根据数组的长度序列化元素,elementData
是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上述的方式来实现序列化,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间。
ArrayList(int initialCapacity)构造方法
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//如果传入的初始容量大于0,就新建一个数组存储元素
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//如果传入的初始化容量等于0,使用空数组EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
} else {
//如果传入的初始化容量小于0,抛出异常。
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}