Java ListIterator详解:双向遍历与安全修改集合
1. ListIterator基础比Iterator更强的双向操作能力Java集合框架中的ListIterator是Iterator接口的增强版本专门为List类型集合设计。与普通Iterator只能单向遍历不同ListIterator具有以下核心特性双向遍历支持hasPrevious()/previous()实现逆向遍历定位查询通过nextIndex()/previousIndex()获取元素位置实时修改允许在遍历过程中直接修改当前元素动态增删提供add()和remove()方法实现集合的动态修改ListString languages new ArrayList(Arrays.asList(Java,Python,Go)); ListIteratorString iterator languages.listIterator();关键区别普通Iterator在遍历时修改集合会抛出ConcurrentModificationException而ListIterator是官方支持的安全修改方式2. 修改集合元素的三种典型场景2.1 条件替换批量修改符合要求的元素当需要根据特定条件替换元素时典型模式如下ListInteger numbers new ArrayList(Arrays.asList(1,2,3,4,5)); ListIteratorInteger 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()方法可以在遍历过程中插入元素ListString fruits new ArrayList(Arrays.asList(Apple,Orange)); ListIteratorString 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的删除更加可控ListDouble prices new ArrayList(Arrays.asList(10.5, 20.0, 15.8)); ListIteratorDouble 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以下情况仍会抛出ConcurrentModificationExceptionListString 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实现元素替换 ListString result list.stream() .map(s - s.equals(old) ? new : s) .collect(Collectors.toList());选择依据简单转换用Stream更简洁复杂条件修改仍需ListIterator并行处理优先考虑Stream3.7 线程安全方案多线程环境下的安全策略ListString 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(ListProduct products) { ListIteratorProduct 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 文本处理中的行级操作日志文件处理示例ListString lines Files.readAllLines(path); ListIteratorString 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 游戏状态管理游戏实体状态更新模式ListGameEntity entities new CopyOnWriteArrayList(); // 主线程 ListIteratorGameEntity 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 ListProcessorT { public final void process(ListT list) { ListIteratorT 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(ListIteratorT it, T item); }这种模式将遍历逻辑与业务处理解耦既保证了操作安全又提高了代码复用性。