2. Java 中的 List 接口有哪些实现类?
在Java中,List接口是一个有序的集合接口,允许包含重复元素,并且支持通过索引来访问元素。List接口有多个实现类,它们各自具有不同的特性和用例。以下是主要的List接口实现类:
1.ArrayList
-
概述:
ArrayList是基于动态数组实现的List接口的实现类。它提供了随机访问元素的能力,查找元素的速度较快。 -
特点
-
支持快速随机访问,时间复杂度为O(1)。
-
插入和删除元素(特别是在末尾)通常较快,但在数组中间插入或删除元素可能需要移动大量数据,时间复杂度为O(n)。
-
默认初始容量为10,当数组满时,容量会自动增加为原来的1.5倍左右。
-
-
用例: 适用于需要频繁读取数据、较少进行中间插入和删除的场景。
-
示例
List<String> arrayList = new ArrayList<>();
2. LinkedList
-
概述:
LinkedList是基于双向链表实现的List接口的实现类。它支持快速的插入和删除操作。 -
特点
-
插入和删除元素较快,尤其是首尾位置的插入和删除,时间复杂度为O(1)。
-
随机访问元素的时间复杂度为O(n),因为需要从头部或尾部遍历链表来找到元素。
-
也实现了
Deque接口,因此可以作为双端队列使用。
-
-
用例: 适用于需要频繁插入和删除元素的场景,而不需要频繁随机访问的情况。
-
示例
List<String> linkedList = new LinkedList<>();
3. Vector
-
概述:
Vector也是基于动态数组实现的List接口的实现类,但它是线程安全的。 -
特点
-
所有操作都是同步的,因此线程安全,但这也使得它的性能相较于
ArrayList稍慢。 -
默认初始容量为10,当数组满时,容量会自动增加为原来的2倍。
-
由于线程安全的设计,
Vector在现代开发中不常用,通常推荐使用ArrayList或者Collections.synchronizedList()来代替。
-
-
用例: 在需要线程安全且没有其他同步机制时可以使用
Vector。 -
示例
List<String> vector = new Vector<>();
4. Stack
-
概述:
Stack是Vector的子类,代表了一个后进先出(LIFO)的栈结构。 -
特点
-
Stack提供了标准的栈操作方法如push()、pop()、peek()等。 -
继承了
Vector的线程安全性,但通常推荐使用Deque接口的实现类如ArrayDeque作为栈来代替Stack类。
-
-
用例: 在需要LIFO栈操作时可以使用
Stack,但推荐使用ArrayDeque或LinkedList来替代它。 -
示例
Stack<String> stack = new Stack<>();
5. CopyOnWriteArrayList
-
概述:
CopyOnWriteArrayList是List接口的线程安全实现,基于“写时复制”机制。 -
特点
-
在每次修改操作(如
add、set等)时,都会创建一个数组的副本,因此读操作是无锁的,并且是非常快的。 -
适合于读操作多于写操作的场景。
-
由于每次修改都会复制数组,所以在频繁修改时开销较大。
-
-
用例: 适用于多线程环境中,读操作频繁且写操作较少的场景。
-
示例
List<String> cowList = new CopyOnWriteArrayList<>();
总结
-
ArrayList: 适合频繁访问元素、不经常插入或删除的场景。 -
LinkedList: 适合频繁插入、删除元素、不需要随机访问的场景。 -
Vector: 一个线程安全的ArrayList,但性能较差,现代开发中较少使用。 -
Stack: 用于LIFO栈结构,但推荐使用ArrayDeque或LinkedList替代。 -
CopyOnWriteArrayList: 适合多线程读操作频繁的场景。
这些实现类在不同的场景下具有不同的优势和劣势,选择合适的List实现类对于编写高效的代码非常重要。
