ArrayList是开发中非常常用的数据存储容器之一,其底层是数组
实现的,我们可以在集合中存储任意类型的数据,ArrayList是线程不安全的,擅长随机访问元素,插入和删除较慢
。
1、ArrayList的数据结构
ArrayList的底层数据结构就是一个数组
,数组元素的类型为Object类型,对ArrayList的所有操作底层都是基于数组的。
2、ArrayList是线程不安全的
对ArrayList进行添加元素的操作的时候是分两个步骤进行的:
- 先在
object[size]
的位置上存放需要添加的元素; - 将
size
的值增加1。
由于这个过程在多线程的环境下是不能保证具有原子性 的,因此ArrayList在多线程的环境下是线程不安全的。
在单线程运行的情况下,如果Size = 0,添加一个元素后,此元素在位置 0,而且Size=1;而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此ArrayList 添加元素,因为此时 Size 仍然等于 0
(注意:我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将 元素存放在位置0。然后线程A和线程B都继续运行,都增 加 Size 的值。 那好,现在我们来看看ArrayList 的情况,元素实际上只有一个,存放在位置 0,而Size却等于 2。这就是“线程不安全”了。
如果非要在多线程的环境下使用ArrayList,就需要保证它的线程安全性,通常有两种解决办法:
- 使用synchronized关键字;
- 可以用Collections类中的静态方法synchronizedList(),对ArrayList进行调用即可。
3、ArrayList的实现
对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。下面我们来分析ArrayList的源代码:
1) 私有属性:
ArrayList定义只定义类两个私有属性:
/**
*ArrayList 的元素存储在其中的数组缓冲区。
*ArrayList 的容量就是这个数组缓冲区的长度。添加第一个元素时,任何带有 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空 ArrayList 都将扩展为 DEFAULT_CAPACITY(10)。
*/
transient Object[] elementData;
// ArrayList 的大小
private int size;
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
有点抽象,看个例子应该能明白:
public class UserInfo implements Serializable {
private static final long serialVersionUID = 996890129747019948L;
private String name;
private transient String psw;
public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
}
public String toString() {
return "name=" + name + ", psw=" + psw;
}
}
public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("张三", "123456");
System.out.println(userInfo);
try {
// 序列化,被设置为transient的属性没有被序列化
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.out"));
o.writeObject(userInfo);
o.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
// 重新读取内容
ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.out"));
UserInfo readUserInfo = (UserInfo) in.readObject();
// 读取后psw的内容为null
System.out.println(readUserInfo.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
被标记为transient的属性在对象被序列化的时候不会被保存。回到ArrayList的分析中
2) 构造方法:
ArrayList提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。
/**
* 构造一个具有指定初始容量的空列表。
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0)