一、迭代器演进:从单向到双向的突破
在Java集合框架中,迭代器是访问容器元素的核心机制。早期Iterator接口仅支持单向遍历(hasNext()/next())和简单删除操作,这种设计在需要动态修改列表的场景中暴露出明显局限:
- 单向遍历缺陷:当需要从后向前检查元素时,必须创建反向索引或使用临时集合存储
- 修改操作受限:删除元素后需手动调整迭代位置,插入元素则可能破坏迭代顺序
- 状态同步难题:多线程环境下修改列表结构易导致ConcurrentModificationException
ListIterator的诞生彻底改变了这一局面。作为Iterator的增强子接口,它通过以下特性重构了列表操作范式:
- 双向导航能力:新增previous()和hasPrevious()方法实现逆向遍历
- 动态修改支持:提供add()、set()、remove()方法直接操作原列表
- 精确位置控制:通过nextIndex()和previousIndex()获取当前游标位置
二、核心方法体系解析
ListIterator接口定义了11个方法,构成完整的双向操作闭环。以下从三个维度解析其方法体系:
1. 双向遍历控制
List<String> list = Arrays.asList("A", "B", "C");ListIterator<String> iterator = list.listIterator();// 正向遍历while(iterator.hasNext()) {System.out.println(iterator.next()); // A → B → C}// 逆向遍历(需先移动到末尾)while(iterator.hasPrevious()) {System.out.println(iterator.previous()); // C → B → A}
关键特性:
- 游标初始位置在列表开头之前(nextIndex()=0)
- 每次next()调用后,游标移动到元素之后
- previous()调用前必须确保hasPrevious()为true
2. 动态修改操作
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3));ListIterator<Integer> it = numbers.listIterator();it.next(); // 定位到1it.add(0); // 在1前插入0 → [0,1,2,3]it.next(); // 移动到2it.set(20); // 修改2为20 → [0,1,20,3]it.remove(); // 删除20 → [0,1,3]
修改操作规则:
- add()在游标当前位置插入新元素
- set()替换最近next()或previous()返回的元素
- remove()删除最近访问的元素
- 修改后nextIndex()/previousIndex()自动调整
3. 索引定位机制
List<String> colors = Arrays.asList("Red", "Green", "Blue");ListIterator<String> it = colors.listIterator(2); // 从索引2开始System.out.println(it.nextIndex()); // 2System.out.println(it.previousIndex()); // 1
索引管理要点:
- 构造时可指定起始索引(必须满足0 ≤ index ≤ size)
- nextIndex()始终等于previousIndex()+1
- 逆向遍历时需注意索引边界检查
三、典型应用场景
1. 列表反转实现
传统反转需要O(n)空间复杂度,使用ListIterator可实现原地反转:
public static <T> void reverseList(List<T> list) {ListIterator<T> forward = list.listIterator();ListIterator<T> backward = list.listIterator(list.size());while(forward.nextIndex() < backward.previousIndex()) {T temp = forward.next();forward.set(backward.previous());backward.set(temp);}}
2. 条件性元素处理
在遍历过程中根据条件动态修改列表:
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));ListIterator<String> it = names.listIterator();while(it.hasNext()) {String name = it.next();if(name.length() > 4) {it.set(name.toUpperCase()); // 长名字转为大写} else {it.add(name + "_short"); // 短名字添加后缀it.previous(); // 保持游标位置}}// 结果: ["Alice_short", "ALICE", "Bob_short", "BOB", "Charlie_short", "CHARLIE"]
3. 多段遍历控制
实现分段处理大型列表:
public static void processInBatches(List<Data> dataList, int batchSize) {ListIterator<Data> it = dataList.listIterator();int batchCount = 0;while(it.hasNext()) {List<Data> batch = new ArrayList<>();while(it.hasNext() && batchCount < batchSize) {batch.add(it.next());batchCount++;}processBatch(batch); // 处理当前批次batchCount = 0;// 跳过已处理元素(若需保留可移除此段)while(batchCount < batch.size() && it.hasPrevious()) {it.previous();it.remove();batchCount++;}}}
四、线程安全实践
ListIterator本身不是线程安全的,在多线程环境下需采用以下策略:
-
同步包装:通过Collections.synchronizedList()包装列表
List<String> syncList = Collections.synchronizedList(new ArrayList<>());ListIterator<String> it = syncList.listIterator();synchronized(syncList) {while(it.hasNext()) {// 操作需在同步块内完成System.out.println(it.next());}}
-
并发集合替代:使用CopyOnWriteArrayList等线程安全集合
List<String> cowList = new CopyOnWriteArrayList<>();ListIterator<String> it = cowList.listIterator(); // 迭代时创建底层数组快照
-
防御性复制:对关键操作创建列表副本
List<Data> snapshot = new ArrayList<>(originalList);ListIterator<Data> it = snapshot.listIterator(); // 操作副本
五、性能优化建议
- 预分配容量:对ArrayList等可扩容集合,初始化时指定足够容量
- 减少结构修改:批量操作前调用listIterator(int index)定位游标
- 避免嵌套迭代:双重迭代时使用普通Iterator外层,ListIterator内层
- 选择合适实现:LinkedList的ListIterator实现比ArrayList更高效
ListIterator通过其独特的双向遍历和动态修改能力,为Java开发者提供了处理有序集合的强大工具。掌握其核心机制和最佳实践,能够显著提升复杂列表操作的效率和代码可维护性。在实际开发中,应根据具体场景选择合适的迭代策略,并特别注意线程安全和性能优化问题。