Java基础 集合框架 抽象类 AbstractList

发布于:2025-07-05 ⋅ 阅读:(20) ⋅ 点赞:(0)


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 对象本身继承自 AbstractListequals(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");//抛出不可操作异常

网站公告

今日签到

点亮在社区的每一天
去签到