1.List概述
List接口
是 Java 集合框架中的一个重要组成部分,主要用于存储一组元素,这些元素可以是重复的,并且可以通过索引访问。List接口
继承了Collection接口
,而Collection接口
又继承了Iterable接口
。这种继承关系使得List接口具备了迭代的能力,即能够通过实现Iterable接口
中的方法来遍历集合中的元素。
2.List常见实现类
3.ArrayList类
-
ArrayList 底层是基于数组实现的,查询元素快,增删相对较慢。
-
适用场景:
适用于需要频繁访问和修改元素的场景,对于频繁插入和删除操作,可能需要考虑其他类型的集合,比如
LinkedList
。 -
构造函数:
// 创建一个空的 ArrayList,初始容量为 10 ArrayList<E> list = new ArrayList<>(); // 创建一个空的 ArrayList,指定初始容量 ArrayList<E> list = new ArrayList<>(initialCapacity); // 创建一个 ArrayList,并用另一个集合初始化 ArrayList<E> list = new ArrayList<>(Collection<? extends E> c);
-
核心方法:
-
添加元素:
add(E e); // 添加到列表末尾 add(int index, E element); // 在指定位置插入元素
-
访问元素:
E get(int index); // 获取指定位置的元素
-
更新元素:
E set(int index, E element); // 替换指定位置的元素
-
删除元素:
E remove(int index); // 删除指定位置的元素 boolean remove(Object o); // 删除第一个匹配的元素
-
获取列表大小:
int size(); // 返回列表中的元素个数
-
检查是否包含元素:
boolean contains(Object o); // 检查列表是否包含指定元素
-
清空列表:
void clear(); // 清空所有元素
-
其他操作:
boolean isEmpty(); // 检查列表是否为空 void trimToSize(); // 修剪列表的容量以匹配其大小
-
-
ArrayList长度可变原理
详情请看这篇文章:ArrayList动态扩容机制(长度可变原理)
4.LinkedList类
-
LinkedList 底层是基于双向链表实现的,查询元素较慢,增删相对于数组较快,首尾操作非常快。
-
特有方法:
-
添加方法:
addLast(E e); //将元素插入到链表的开头 addFirst(E e); //将元素插入到链表的末尾
-
删除方法:
removeFirst(); //移除并返回链表的第一个元素 removeLast(); //移除并返回链表的最后一个元素
-
访问方法:
getFirst() //返回链表的第一个元素 getLast() //返回链表的最后一个元素
-
-
LinkedList里面的==get()==方法是如何实现的:
-
源码:
//get()方法 public E get(int index) { //检查索引是否在有效范围内,即是否在 0 到 size-1 的范围内 //不在索引范围内,抛出IndexOutOfBoundsException异常 checkElementIndex(index); // return node(index).item; } -------------------------------------------------------------------- //checkElementIndex()方法,get()方法里调用了该方法 private void checkElementIndex(int index) { //用于判断给定的索引是否在链表的有效范围内 //!:取反 if (!isElementIndex(index)) //ture时(index不在索引范围内时),抛出异常 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } ------------------------------------------------------------------------ //isElementIndex()方法,checkElementIndex()方法里调用了该方法 private boolean isElementIndex(int index) { //如果index大于等于0并且小于链表的大小size,则返回true,否则返回false return index >= 0 && index < size; } ----------------------------------------------------------------------- //node()方法,get()方法里调用了该方法 //返回链表中指定索引位置的节点,根据二分法查找 Node<E> node(int index) { // 判断索引位置是否接近链表的前端,如果是,就从前端开始遍历 // >> 1 : 向右位移一位,等同于 size / 2,用于快速计算链表的中间位置 if (index < (size >> 1)) { Node<E> x = first; //链表的第一个节点 first for (int i = 0; i < index; i++) x = x.next; //x.next 表示链表中的下一个节点 return x; } else {//反之,就从后端开始遍历 Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; //x.prev 表示链表中的前一个节点 return x; } }
-
5.Vector类
Vector
类是一个动态数组的实现,早期的 Java 版本引入了这个类,用于动态存储元素,类似于 ArrayList
。不过,它与 ArrayList
有一些不同之处。
主要特点
- 动态调整大小:
Vector
内部使用一个可调整大小的数组来存储元素。当数组空间不足时,Vector
会自动扩展数组的大小。 - 同步:
Vector
是线程安全的。所有对Vector
的操作都是同步的,这意味着它可以在多线程环境中安全地使用,但也可能会导致性能开销。 - 继承关系:
Vector
继承自AbstractList
类,并实现了List
接口。由于继承自AbstractList
,它也实现了RandomAccess
接口,支持快速随机访问。 - 初始容量和增量:
Vector
允许设置初始容量和扩展增量。初始容量是创建Vector
时的初始数组大小,而增量是每次数组需要扩展时增加的大小。
常用方法
add(E e)
:在Vector
的末尾添加元素。remove(Object o)
:移除第一个出现的指定元素。get(int index)
:获取指定索引位置的元素。size()
:返回Vector
中的元素数量。
6.Stack类
Stack
类是 Vector
类的一个子类,用于实现栈的数据结构。栈遵循后进先出(LIFO)的原则,即最后添加的元素最先被移除。
主要特点
- 继承自
Vector
:Stack
类继承了Vector
,因此它也具有Vector
的所有特性,包括同步性。 - 栈操作:提供了标准的栈操作方法,如
push
、pop
和peek
。 - 同步:
Stack
的方法是线程安全的,因此可以在多线程环境中安全使用。
常用方法
push(E item)
:将元素推入栈中。pop()
:移除并返回栈顶的元素。peek()
:返回栈顶的元素,但不移除它。isEmpty()
:检查栈是否为空。
7.CopyOnWriteArrayList类
CopyOnWriteArrayList
是一个线程安全的 List
实现,采用了写时复制(Copy-on-Write)策略。这种策略使得它适用于读多写少的场景。
主要特点
- 线程安全:
CopyOnWriteArrayList
是线程安全的。写操作会复制底层数组,因此写操作不会影响读操作。 - 写时复制:每次写操作(如添加、删除元素)都会创建底层数组的副本,因此写操作的开销相对较大,但读操作是非常高效的。
- 适用于读多写少的场景:由于读操作不需要同步,
CopyOnWriteArrayList
在读多写少的应用场景中表现很好。
常用方法
add(E e)
:将元素添加到列表末尾。remove(Object o)
:移除第一次出现的指定元素。get(int index)
:获取指定索引位置的元素。size()
:返回列表中的元素数量。
除元素)都会创建底层数组的副本,因此写操作的开销相对较大,但读操作是非常高效的。
- 适用于读多写少的场景:由于读操作不需要同步,
CopyOnWriteArrayList
在读多写少的应用场景中表现很好。
常用方法
add(E e)
:将元素添加到列表末尾。remove(Object o)
:移除第一次出现的指定元素。get(int index)
:获取指定索引位置的元素。size()
:返回列表中的元素数量。