AbstractList
AbstractList 是 实现 List 接口的核心抽象类,通过 继承AbstractCollection实现集合的部分模板方法,并提供部分通用方法的默认实现。其设计体现了 模板方法模式的思想,将 可变部分(如 get() 和 size()) 留给子类实现, 固定部分(如迭代器、工具方法)由父类封装
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
核心抽象方法 (必须由子类实现)
必须由子类实现的核心方法,这是因为子类的数据结构可能不一样,需要由具体的数据结构定义
abstract public E get(int index); 获取指定索引位置的元素。这是实现随机访问的基础
public abstract int size();返回列表中元素的数量
可选择实现抽象方法 (如果支持修改):
//这三个方法都是直接抛出不可操作异常
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
重要提示: 默认实现的 iterator() 和 listIterator() 返回的迭代器依赖于 remove() 方法(如果子类不
AbstractList 对于集合有序的定位
public boolean add(E e) {
add(size(), e); // 默认在末尾添加元素
return true;
}
// 基于元素的位置添加元素
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
indexOf(Object o), lastIndexOf(Object o): 通过迭代查找元素位置
也就是说对于Lis集合的定位是有序
的,这种有序默认的不是自然排序,而是按照元素添加的插入顺序
//同样删除也是根据指定 索引 位置
remove(int index);
//迭代器
iterator(): 返回基于 get() 和 size() 实现的迭代器 (ListItr)。子类通常可以提供更高效的实现。
listIterator(), listIterator(int index): 返回列表迭代器 (ListItr),同样基于 get(), size(), set() (如果支持), add() (如果支持), remove() (如果支持) 实现。
indexOf(Object o), lastIndexOf(Object o): 通过迭代查找元素。
add(E e): 默认实现在末尾添加元素(调用 add(size(), e))。
addAll(int index, Collection c): 在指定位置添加一个集合的所有元素。
removeRange(int fromIndex, int toIndex): 移除指定索引范围内的元素(受保护方法,供 clear() 和子列表使用)。
subList(int fromIndex, int toIndex): 返回基于当前列表的视图(子列表)。返回的 SubList 对象本身继承自 AbstractList。
equals(Object o), hashCode(): 提供符合 List 接口规范的实现(比较元素顺序和内容)。
clear(): 默认通过调用 removeRange(0, size()) 实现。
contains(Object o): 通过 indexOf(o) >= 0 实现
支持集合的快速失败(Fail-Fast)机制
支持快速失败(Fail-Fast)机制:通过维护 modCount(结构修改次数) 字段,确保迭代过程中检测到 并发修改时抛出 ConcurrentModificationException
//初始更改状态为0 每次集合变动累加1
protected transient int modCount = 0;
//再迭代过程中 检查变动更新值,比如迭代开始值为0,迭代过程中有新增元素值则变更为1
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这里也能看出,线程不安全:AbstractList 默认不支持多线程并发访问,需外部同步
AbstractList 的迭代器
迭代器的核心功能方法就那么几个,迭代器的详情介绍
①.hasNext():检查集合中是否还有下一个元素。
②.next():获取集合中的下一个元素。
③.remove():从集合中移除上一次 next() 方法返回的元素
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> it = list.iterator();
// 正向遍历,从前往后进行遍历
while (it.hasNext()) {
String element = it.next();
if (element.equals("B")) {
it.remove(); // 移除 "B"
}
}
System.out.println(list); // 输出 [A, C]
基础迭代器 iterator
AbstractList提供了 iterator() 的默认实现,通过 iterator() 返回的迭代器基于get() 和 size() 方法遍历元素,而 get() 和 size() 方法 由具体数据结构类型的子类跟着自身数据结构提供
,典型的模板方法模式的设计模式
先看下基础迭代器 iterator的应用过程
// 伪代码示例,后面有关于内部类的代码说明
public Iterator<E> iterator() {
return new Itr(); // 默认实现
}
private class Itr implements Iterator<E> {
int cursor = 0;
public boolean hasNext() {
checkForComodification();//支持快速失败(Fail-Fast)机制
return cursor < size(); // ← 这里调用抽象方法
}
public E next() {
checkForComodification();//支持快速失败(Fail-Fast)机制
E next = get(cursor); // ← 这里调用抽象方法
cursor++;
return next;
}
}
运行机制:如子类 ArrayList 继承 AbstractList 时
public class ArrayList<E> extends AbstractList<E> {
// 必须实现抽象方法
public E get(int index) {
return elementData[index];
}
public int size() {
return size;
}
// 不需要重写 iterator()
}
具体实现
//1.JVM 会找到实际对象类型(如 ArrayList)
List<String> stringList = new ArrayList<>();
// 2.调用添加元素方法,具体实现调用add(size(), e);
stringList.add("1");
// 3.通过迭代器的 hasNext() 调用的是 ArrayList 实现的 size()
}
这种设计模式好处:
Ⅰ.减少重复代码
(所有 List 实现类共享迭代器逻辑),如快速失败机制只需写一次
Ⅱ.新实现的 List 只需关注核心数据访问
,只需要根据数据结构特性重写5个核心方法
Ⅲ.但又不强制
子类必须自己实现迭代器,仅提供一种默认的实现方式,如有特殊需要可重写
基础Itr迭代器 的完整方法示例
private class Itr implements Iterator<E> {
int cursor = 0; // 当前遍历位置
int lastRet = -1; // 最近访问的索引(用于remove操作)
int expectedModCount = modCount; // 并发修改检查标记
public boolean hasNext() {
return cursor != size(); // ← 调用子类实现的 size()
}
public E next() {
checkForCommodification(); // 检查并发修改
E next = get(cursor); // ← 调用子类实现的 get()
lastRet = cursor++;
return next;
}
public void remove() {
if (lastRet == -1) throw new IllegalStateException();
checkForCommodification();
AbstractList.this.remove(lastRet); // 调用外部类的 remove
expectedModCount = modCount; // 更新修改计数
cursor = lastRet;
lastRet = -1;
}
final void checkForCommodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
设计亮点:
- 快速失败机制:通过比较
modCount
(外部类修改计数器)和expectedModCount
(迭代器快照)实现 - 资源复用:所有基于
AbstractList
的子类自动获得迭代器能力,无需重复实现 - 子类实现核心功能:获取元素
get(cursor)
和数量size()
的方法又调用子类实现方式,让子类根据自身数据结构实现核心功能
增强迭代器 ListIterator
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index; // 支持从指定位置开始遍历
}
public boolean hasPrevious() {
return cursor != 0;
}
public E previous() {
checkForCommodification();
E previous = get(--cursor); // ← 反向遍历
lastRet = cursor;
return previous;
}
public void add(E e) { /*...*/ } // 支持添加操作
public void set(E e) { /*...*/ } // 支持修改操作
}
功能扩展:
- 双向遍历:ListIterator 支持双向遍历,即可以从前往后遍历,也可以从后往前遍历
- 添加和修改元素:ListIterator 提供 add() 方法和 set() 方法,在遍历过程中向列表中添加修改元素
- 索引访问:ListIterator 提供了 nextIndex() 和 previousIndex() 方法,可以获取当前位置的索引
- 继承自
Itr
的并发修改检查机制 和 迭代器基础功能
基础迭代器和增强迭代器联系与区别
增强迭代器listIterator 继承 基础迭代器iterator的基本迭代功能,在此基础之上,增强迭代器基于自身的数据结构特性新增了功能
,这里针对的是List集合,最大的特性是有序,所以每个元素都有对应的索引,支持从指定位置开始遍历
AbstractList对子列表SubList 视图的支持
class RandomAccessSubList<E> extends SubList<E> implements RandomAccess {
RandomAccessSubList(AbstractList<E> list, int fromIndex, int toIndex) {
super(list, fromIndex, toIndex);
}
// AbstractList的 截取子列表subList方法,包含起位置 和 截停位置
public List<E> subList(int fromIndex, int toIndex) {
return new RandomAccessSubList<>(this, fromIndex, toIndex);
}
}
关键点
1.是视图而非副本
subList()
返回的子列表是原始列表的动态视图
(非独立副本),所有操作直接映射到原始列表的相应范围
List<String> mainList = new ArrayList<>();
mainList.add("A");
mainList.add("B");
mainList.add("C");
mainList.add("D");
List<String> sub = mainList.subList(1, 3);
sub.set(0, "X"); // 修改视图
System.out.println(mainList); // 输出:[A, X, C, D](原始列表同步修改)
修改联动:子列表操作直接影响父列表
2.索引映射
子列表的索引 [0, size-1] 动态映射
到原始列表的[fromIndex, toIndex-1]
// mainList.subList(1, 3)
sub.get(0); // 等价于 mainList.get(1)
3.支持快速失败机制 包括影响父列表
//子列表创建时记录原始列表的 modCount 每次操作前校验:
if (parent.modCount != this.modCount) {
throw new ConcurrentModificationException();
}
// 而且子列表更新操作 父列表也会根据索引值直接变动
代码示例展示
List<String> mainList = new ArrayList<>();
mainList.add("A");
mainList.add("B");
mainList.add("C");
mainList.add("D");
List<String> sub = mainList.subList(1, 3);
for(String str:mainList){ //抛出ConcurrentModificationException异常
sub.add("E");
System.out.println(str);
}
4.使用陷阱
1.除了上面联动修改抛出异常
2.截取子列表视图时候 索引值范围不合理的设置
List<String> mainList = new ArrayList<>();
mainList.add("A");
mainList.add("B");
mainList.add("C");
mainList.add("D");
// 批量操作列表的局部范围
mainList.subList(1, 3).clear(); // 删除索引1-3的元素
//抛出越界异常IndexOutOfBoundsException: toIndex = 3
mainList.subList(0, 3).replaceAll(String::toUpperCase);
5.防止影响父列表的解决方式
将父列表设置成 不可变集合
// jdk1.9版本之后使用list.of方法设置 成不可变集合
List<String> mainList = new ArrayList<>(List.of("A", "B", "C", "D"))
// jek1.8版本使用
List<String> mainList = new ArrayList<>();
mainList.add("A");
//......
List<String> unmodifiable = Collections.unmodifiableList(mainList);
//截取子列表 如果这时候再去操作子列表就报错了
List<String> sub = mainList.subList(1, 3);
sub.set(0, "X");//抛出不可操作异常