ArrayList底层原理、线程安全及其相关集合(面试常问)

发布时间 2023-08-07 22:09:18作者: 單色不單調

一、ArrayList底层原理

1.特点及其原理:ArrayList底层基于数组实现,查找快,增删慢

8a3a04c1999d4f2b882b0fcb99a20923

2.ArrayList底层原理,初始化及调用add()方法添加元素:
默认初始化容量为10

e1728b4b542847019214512d858ef8d6

第一次创建集合并在添加第一个元素时在底层创建一个默认长度为10的数组: ​

调用构造函数初始化时默认创建的是空数组

4abaaba339ff4c1699f99903154a8f1d

c5d33ed6c5f24916ad782d58527964c3

只有在调用add方法添加第一个元素时才会在底层创建默认长度为10的数组

1bbaab44c34e4cf7a8f937a9eb48c270

41a00f2296f547759705f7ea966bd4df

48acf6a5397a4d088fa8cb48973ebb68

总结:第一次创建集合并添加第一个元素时在底层创建一个默认长度为10的数组;当元素插满,会进行扩容,每次扩容到之前的1.5倍(oldCapacity + (oldCapacity >> 1,二进制右移即除二,左移乘二),如果扩容1.5倍之后容量还不够,则将所需要的最小容量minCapacity赋给数组新的容量值newCapacity;扩容之后通过数组的拷贝(使用Arrays.copyOf)来确保元素的准确性。

二、ArrayList线程安全问题

由上图add()方法可知,在ArrayList中的add方法没有做相关线程安全的处理,故ArrayList本身是线程不安全的,与之相关的线程安全的方法有:

1.Vector类

Vector和ArrayList语法相同,都继承了同一个类实现了相同的接口

7d9120c169b349d69f50860ffe4fba26

d98b70993dd94034a374ff6a94700277

Vector中的add()方法添加了synchronized关键字,故Vector类是线程安全的

4811c07675df41bfb9db42a2d373781e

但是由于Vector是直接在方法上使用synchronized锁住整个方法,并发效率低,在高并发的情况下容 易造成线程阻塞等问题。

2.使用Collections工具类

使用集合的工具类Collections提供的synchronizedList方法将线程不安全的集合类变为安全的

//将线程不安全的集合对象作为参数传入synchronizedXxx()方法中
List synchronizedList = Collections.synchronizedList(arrayList);

a53c478aef1947c2854a249bb4e890f7

新new一个线程安全的SynchronizedRandomAccessList或SynchronizedList。当传入的 list 是ArrayList 时,返回 SynchronizedRandomAccessList 对象;传入 LinkedList 时,返回SynchronizedList 对象。(因为ArrayList实现了RandomAccess接口而 LinkedList 没有)

99aab8646eb249d2a9fca4a07ba0d8b3

而SynchronizedRandomAccessList又继承了SynchronizedList,在调用SynchronizedRandomAccessList时构造器会调用super(list)即SynchronizedList类

32af7d7afac34ef694d5b144884af0c0

可以看到SynchronizedList中的add、get、remove等方法内都加了mutex对象锁,从而实现线程安全。

与Vector比较,Vector直接锁住整个方法,而使用synchronizedList时锁住方法内部的部分代码,效率会高一点。

3.在大量并发情况下使用CopyOnWriteArrayList提高集合的效率和安全

d5873c6ae5d2476380884048ccaf91bf

使用Lock锁相较于Synchronized关键字更为灵活、效率更高;使用transient保证值被修改后,其它线程能够立马感知到最新值。且CopyOnWriteArrayList中只有写操作加了lock锁,而读操作(get方法)未加锁,从而提高读取效率(而synchronizedList和Vector中的get方法均加了synchronized关键字):

9ae34de6a5f948c3854a97c68649e5ac

从 add ()方法中可以看出,CopyOnWriteArrayList 通过volatile、加锁 、 以及数组拷贝来保证了集合的线程安全。

60f81ae2bebc4c5fb9f5d6fd3a4cc01d

总结:CopyOnWriteArrayList是一个读操作时无锁,写操作时使用volatile关键字、lock锁以及数组拷贝来保证线程安全的ArrayList。

三、ArrayList与相关集合的区别

1.ArrayList和LinkedList

  • ArrayList底层基于数组,LinkedList底层基于双链表

  • 广泛上来说:ArrayList适合查询,LinkedList适合插入,但是如果获取的是第一个元素,或最后一个元素,LinkedList查询也很快,因为LinkedList中有两个属性分别记录了当前链表中的头结点和尾结点。
    06e8462f4e8c4d3a9161ea4f250dd72a

  • ArrayList实现了RandomAccess接口,以便在随机访问列表或顺序访问列表时提供良好的性能;而LinkedList没有实现该接口。

    2.ArrayList和Vector

  • ArrayList同一时间允许多个线程进行访问,效率高、但是线程不安全,而Vector是线程安全的,但是效率低

  • ArrayList扩容是每次扩大到原来的1.5倍,Vector扩容是增加原来的两倍

  • ArrayList可使用集合工具类Collections的synchronizedList方法将线程不安全的ArrayList集合变为安全的,所以现在很少会使用到Vector