1. ListIterator基础:比Iterator更强的双向操作能力
Java集合框架中的ListIterator是Iterator接口的增强版本,专门为List类型集合设计。与普通Iterator只能单向遍历不同,ListIterator具有以下核心特性:
- 双向遍历:支持hasPrevious()/previous()实现逆向遍历
- 定位查询:通过nextIndex()/previousIndex()获取元素位置
- 实时修改:允许在遍历过程中直接修改当前元素
- 动态增删:提供add()和remove()方法实现集合的动态修改
List<String> languages = new ArrayList<>(Arrays.asList("Java","Python","Go")); ListIterator<String> iterator = languages.listIterator();关键区别:普通Iterator在遍历时修改集合会抛出ConcurrentModificationException,而ListIterator是官方支持的安全修改方式
2. 修改集合元素的三种典型场景
2.1 条件替换:批量修改符合要求的元素
当需要根据特定条件替换元素时,典型模式如下:
List<Integer> numbers = new ArrayList<>(Arrays.asList(1,2,3,4,5)); ListIterator<Integer> it = numbers.listIterator(); while(it.hasNext()) { int num = it.next(); if(num % 2 == 0) { it.set(num * 10); // 将偶数放大10倍 } } // 结果:[1, 20, 3, 40, 5]实现要点:
- 必须先调用next()移动游标
- set()修改的是最后一次访问的元素
- 修改后迭代器位置不会改变
2.2 动态插入:在特定位置添加新元素
利用add()方法可以在遍历过程中插入元素:
List<String> fruits = new ArrayList<>(Arrays.asList("Apple","Orange")); ListIterator<String> it = fruits.listIterator(); while(it.hasNext()) { String fruit = it.next(); if(fruit.equals("Orange")) { it.add("Banana"); // 在Orange后插入 } } // 结果:[Apple, Orange, Banana]注意:add()操作后,下一次调用next()会返回新插入元素的后继元素
2.3 条件删除:安全移除特定元素
与普通Iterator的remove()不同,ListIterator的删除更加可控:
List<Double> prices = new ArrayList<>(Arrays.asList(10.5, 20.0, 15.8)); ListIterator<Double> it = prices.listIterator(); while(it.hasNext()) { double price = it.next(); if(price < 16) { it.remove(); // 删除小于16的元素 } } // 结果:[20.0]删除逻辑:
- remove()删除的是最后一次next()或previous()返回的元素
- 连续remove()前必须再次移动迭代器
- 与set()互斥,不能连续调用
3. 实战中的七个关键技巧
3.1 游标位置控制
ListIterator的游标概念容易混淆,图示说明:
元素: [A, B, C, D] 游标: ^ 初始位置 next()返回A,游标移动到A和B之间定位技巧:
- nextIndex()返回下一次next()将访问的索引
- previousIndex()返回下一次previous()将访问的索引
3.2 复合操作的安全顺序
错误示例:
it.next(); it.remove(); it.add("New"); // 抛出IllegalStateException正确顺序:
- 先移动(next/previous)
- 再修改(set/add/remove)
- 同类型操作不能连续执行
3.3 并发修改检测机制
即使使用ListIterator,以下情况仍会抛出ConcurrentModificationException:
List<String> list = new ArrayList<>(...); ListIterator it = list.listIterator(); list.add("外部修改"); // 其他方式修改集合 it.next(); // 检测到并发修改解决方案:
- 遍历期间通过迭代器独占修改权
- 需要外部修改时,重新获取迭代器
3.4 性能优化建议
对于大型集合:
- 避免频繁的add/remove操作
- LinkedList的ListIterator性能优于ArrayList
- 批量修改考虑使用subList()
3.5 特殊集合的适配
不同List实现类的差异:
| 集合类型 | ListIterator特性 |
|---|---|
| ArrayList | 快速随机访问,修改代价高 |
| LinkedList | 插入删除高效,访问速度慢 |
| CopyOnWriteArrayList | 快照迭代器,不支持修改操作 |
3.6 与Java8 Stream的对比
现代Java开发中的替代方案:
// 使用Stream实现元素替换 List<String> result = list.stream() .map(s -> s.equals("old") ? "new" : s) .collect(Collectors.toList());选择依据:
- 简单转换用Stream更简洁
- 复杂条件修改仍需ListIterator
- 并行处理优先考虑Stream
3.7 线程安全方案
多线程环境下的安全策略:
List<String> syncList = Collections.synchronizedList(new ArrayList<>()); // 必须手动同步 synchronized(syncList) { ListIterator it = syncList.listIterator(); while(it.hasNext()) { it.set(...); } }或者使用CopyOnWriteArrayList(适合读多写少场景)
4. 典型问题排查指南
4.1 IllegalStateException异常
现象:调用set()/remove()时抛出"Invalid state for operation"
原因分析:
- 未先调用next()或previous()
- 前一个操作是add()
- 前一个操作是remove()
解决方案:
- 确保每次修改前都有移动操作
- 检查操作顺序是否符合规范
4.2 元素修改未生效
常见情况:
- set()后集合内容未改变
- add()后元素未出现在预期位置
排查步骤:
- 确认迭代器是否来自目标集合
- 检查是否调用了正确的修改方法
- 验证游标位置是否符合预期
4.3 遍历结果异常
典型表现:
- 元素重复处理
- 某些元素被跳过
调试方法:
System.out.println("当前索引:" + it.nextIndex() + " 值:" + it.next()); // 在修改操作前后打印状态4.4 性能问题优化
对于超大型集合的优化技巧:
- 使用List.listIterator(int)从中间开始
- 批量操作时考虑使用subList()
- 改用LinkedList提高修改效率
5. 实际工程应用案例
5.1 分页处理中的动态过滤
电商平台商品过滤场景:
public void filterInactiveProducts(List<Product> products) { ListIterator<Product> it = products.listIterator(); while(it.hasNext()) { Product p = it.next(); if(!p.isActive()) { it.remove(); } else if(p.needsUpdate()) { it.set(p.refresh()); } } }5.2 文本处理中的行级操作
日志文件处理示例:
List<String> lines = Files.readAllLines(path); ListIterator<String> it = lines.listIterator(); while(it.hasNext()) { String line = it.next(); if(line.contains("ERROR")) { it.set(line + " [已标记]"); it.add("StackTrace: ..."); } } Files.write(path, lines);5.3 游戏状态管理
游戏实体状态更新模式:
List<GameEntity> entities = new CopyOnWriteArrayList<>(); // 主线程 ListIterator<GameEntity> it = entities.listIterator(); while(it.hasNext()) { GameEntity e = it.next(); if(e.isExpired()) { it.remove(); } else { e.update(); } }6. 扩展知识:与其它语言的对比
6.1 C++ STL中的迭代器
C++的迭代器设计更底层:
- 支持指针算术运算
- 分类更细致(前向、双向、随机访问)
- 失效规则更复杂
6.2 Python的迭代器协议
Python采用__next__()协议:
- 没有显式的修改接口
- 通过生成器实现类似功能
- 更强调不可变遍历
6.3 JavaScript的Array迭代器
ES6引入的迭代器特点:
- Symbol.iterator协议统一
- 没有修改原数组的能力
- 新增的entries()/keys()方法
7. 最佳实践总结
经过多年项目实践,我总结出ListIterator的黄金法则:
- 明确游标状态:每次操作前确认迭代器位置
- 单一职责原则:一次遍历只做一种修改操作
- 防御性编程:总是检查hasNext()/hasPrevious()
- 资源管理:短生命周期使用迭代器
- 性能预判:根据集合类型选择最优策略
对于复杂业务逻辑,建议采用模板方法模式封装通用操作:
public abstract class ListProcessor<T> { public final void process(List<T> list) { ListIterator<T> it = list.listIterator(); while(it.hasNext()) { T item = it.next(); if(shouldProcess(item)) { processItem(it, item); } } } protected abstract boolean shouldProcess(T item); protected abstract void processItem(ListIterator<T> it, T item); }这种模式将遍历逻辑与业务处理解耦,既保证了操作安全,又提高了代码复用性。