当前位置: 首页 > news >正文

052_迭代器(Iterator / ListIterator)

一、迭代器概述

迭代器(Iterator)是 Java 集合框架中用于遍历集合元素的工具接口,它提供了一种统一的遍历方式,屏蔽了不同集合底层数据结构的差异(如数组、链表、哈希表等)。其核心思想是 “迭代器负责遍历,集合负责存储”,通过迭代器可以安全、高效地访问集合中的元素。

Java 中主要的迭代器接口包括:

  • Iterator:基础迭代器,支持单向遍历和元素删除。
  • ListIterator:Iterator的子接口,仅适用于List集合,支持双向遍历、元素添加和修改。

迭代器解决了传统for循环遍历的两个核心问题:

  • 避免暴露集合底层实现(无需通过索引访问,适配所有集合类型)。
  • 提供遍历过程中的元素修改安全性(通过迭代器自身的remove()方法,避免并发修改异常)。

在这里插入图片描述

二、Iterator 详解

2.1 核心定义与作用

Iterator是所有迭代器的基础接口,定义在java.util包中,仅支持单向遍历(从集合头部到尾部),适用于所有Collection接口的实现类(如List、Set、Queue等)。

通过集合的iterator()方法可获取迭代器实例,核心流程为:

  1. 调用hasNext()判断是否有下一个元素。
  2. 调用next()获取下一个元素(并移动迭代器指针)。
  3. (可选)调用remove()删除当前元素(需在next()之后调用)。

2.2 核心方法

方法定义功能说明
boolean hasNext()判断集合中是否还有未遍历的元素,有则返回true,否则返回false。
E next()返回集合中的下一个元素,并将迭代器指针向后移动一位。若没有下一个元素,抛出NoSuchElementException。
void remove()删除next()方法最后返回的元素(需在next()之后调用,否则抛出IllegalStateException)。

2.3 使用示例

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class IteratorDemo {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");// 获取迭代器Iterator<String> iterator = list.iterator();// 遍历集合while (iterator.hasNext()) {String element = iterator.next(); // 获取下一个元素System.out.println(element);// 示例:删除"Banana"if ("Banana".equals(element)) {iterator.remove(); // 仅能通过迭代器删除,避免并发修改异常}}System.out.println("删除后集合:" + list); // 输出:[Apple, Cherry]}
}

2.4 核心特点

特点说明
单向遍历仅支持从集合头部到尾部的顺序遍历(hasNext()+next()),无法向前遍历。
通用性适用于所有Collection实现类(List、Set、Queue等),兼容性强。
元素删除支持通过remove()方法删除当前元素,但需在next()之后调用(依赖next()记录当前元素位置)。
并发安全遍历过程中通过迭代器的remove()修改集合,不会触发ConcurrentModificationException(直接修改集合会报错)。
无添加 / 修改能力仅能遍历和删除元素,无法向集合中添加或修改元素。

在这里插入图片描述

三、ListIterator 详解

3.1 核心定义与作用

ListIterator是Iterator的子接口,仅适用于List接口的实现类(如ArrayList、LinkedList等),扩展了Iterator的功能,支持双向遍历元素添加 / 修改,是List集合特有的迭代器。

通过List的listIterator()方法可获取实例,若传入索引参数(如listIterator(int index)),迭代器指针会从指定索引位置开始遍历,进一步增强了遍历的灵活性。

3.2 核心方法(扩展自 Iterator)

方法定义功能说明
boolean hasPrevious()判断集合中是否有上一个未遍历的元素(向前遍历),有则返回true。
E previous()返回集合中的上一个元素,并将迭代器指针向前移动一位。若没有上一个元素,抛出NoSuchElementException。
void add(E e)在当前指针位置插入元素(插入后元素位于next()返回元素之前,previous()返回元素之后)。
void set(E e)修改next()或previous()方法最后返回的元素(需在next()/previous()之后调用)。
int nextIndex()返回下一个元素的索引(若没有下一个元素,返回集合大小)。
int previousIndex()返回上一个元素的索引(若没有上一个元素,返回-1)。

3.3 使用示例

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;public class ListIteratorDemo {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("A");list.add("B");list.add("C");// 获取ListIterator(从索引1开始遍历)ListIterator<String> lit = list.listIterator(1);System.out.println("向后遍历:");while (lit.hasNext()) {int index = lit.nextIndex(); // 获取下一个元素索引String element = lit.next();System.out.println("索引" + index + ":" + element); // 输出:索引1:B,索引2:C}System.out.println("向前遍历:");while (lit.hasPrevious()) {int index = lit.previousIndex(); // 获取上一个元素索引String element = lit.previous();System.out.println("索引" + index + ":" + element); // 输出:索引1:B,索引0:A}// 在当前位置(索引0后)插入元素lit.add("D");System.out.println("插入后集合:" + list); // 输出:[A, D, B, C]// 修改下一个元素(当前指针指向索引1的"D")lit.next(); // 移动到"D"lit.set("D-Updated");System.out.println("修改后集合:" + list); // 输出:[A, D-Updated, B, C]}
}

3.4 核心特点

特点说明
双向遍历支持向前(hasPrevious()+previous())和向后(hasNext()+next())遍历,灵活性更高。
元素操作扩展除删除外,支持add()插入元素和set()修改元素,丰富了集合的修改方式。
索引定位可通过nextIndex()/previousIndex()获取当前指针位置索引,便于精准操作。
起始位置可控支持从指定索引开始遍历(listIterator(int index)),无需从头遍历。
仅适用于 List依赖List的索引特性,无法用于Set、Queue等非 List 集合。

在这里插入图片描述

四、Iterator 与 ListIterator 对比

维度IteratorListIterator
继承关系顶层接口,无父接口继承Iterator接口,是子接口
适用集合所有Collection实现类(List、Set、Queue等)仅List实现类(ArrayList、LinkedList等)
遍历方向单向(仅向后:hasNext()+next())双向(向后 + 向前:hasNext()/hasPrevious())
核心操作遍历(hasNext()/next())、删除(remove())遍历、删除、添加(add())、修改(set())
索引支持无索引相关方法有nextIndex()/previousIndex()获取索引
起始位置控制仅能从集合头部开始遍历可通过listIterator(int index)指定起始索引
并发修改安全性支持通过remove()安全删除支持remove()/add()/set()安全修改

5.1 并发修改异常(ConcurrentModificationException)

遍历过程中若直接通过集合的方法(如add()、remove())修改集合结构,迭代器会检测到并发修改,抛出ConcurrentModificationException。正确做法是通过迭代器自身的方法修改集合

  • Iterator:仅能通过remove()删除元素。
  • ListIterator:可通过remove()/add()/set()修改元素。

错误示例(直接修改集合)

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> it = list.iterator();while (it.hasNext()) {String elem = it.next();if ("B".equals(elem)) {list.remove(elem); // 直接调用集合的remove(),抛出异常}
}

正确示例(通过迭代器修改)

// 使用Iterator的remove()
while (it.hasNext()) {String elem = it.next();if ("B".equals(elem)) {it.remove(); // 迭代器删除,安全无异常}
}// 使用ListIterator的add()
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {if ("C".equals(lit.next())) {lit.add("D"); // 迭代器添加,安全无异常}
}

5.2 迭代器状态依赖

  • Iterator的remove()必须在next()之后调用(需先通过next()获取元素,迭代器记录当前元素位置),否则抛出IllegalStateException。
  • ListIterator的set()必须在next()或previous()之后调用(需先获取待修改元素),add()无此限制,但add()后调用remove()会报错(add()插入的元素未被next()/previous()记录)。

5.3 性能考量

  • Iterator遍历Set集合时性能最优(无需索引支持)。
  • ListIterator在LinkedList中双向遍历性能优于通过索引遍历(避免LinkedList索引访问的O(n)开销)。

六、适用场景总结

场景需求推荐迭代器原因分析
遍历Set或Queue集合Iterator这些集合无索引,Iterator是唯一通用遍历方式。
单向遍历List集合,仅需读取Iterator功能足够,无需额外扩展,性能略优。
双向遍历List集合ListIterator支持向前 / 向后遍历,满足复杂遍历需求(如从中间位置开始遍历)。
遍历List时需插入 / 修改元素ListIterator提供add()/set()方法,是List集合修改元素的安全方式。
多线程环境下遍历集合Iterator/ListIterator迭代器自身的修改方法(remove()/add())可避免并发修改异常。

七、总结

迭代器是 Java 集合遍历的核心工具,Iterator提供了基础的单向遍历和删除能力,适用于所有集合;ListIterator作为List特有的迭代器,扩展了双向遍历和元素添加 / 修改功能,灵活性更强。

使用时需根据集合类型和需求选择合适的迭代器:通用场景选Iterator,List的复杂遍历和修改场景选ListIterator。同时需注意遍历过程中通过迭代器自身方法修改集合,避免并发修改异常,确保遍历的安全性和高效性。

掌握迭代器的使用不仅能统一集合遍历方式,更能深入理解 Java 集合框架的设计思想 ——“接口抽象,实现分离”,提升代码的通用性和可维护性。

http://www.lryc.cn/news/595390.html

相关文章:

  • The Survey of Few-shot Prompt Learning on Graph
  • Ubuntu 22.04编译安装Nginx 1.28
  • Vue3 面试题及详细答案120道 (1-15 )
  • 可变形卷积神经网络详解:原理、API与实战
  • 如何使用终端查看任意Ubuntu的版本信息
  • ACE 插入元件
  • 信息学奥赛一本通 1576:【例 2】选课 | 洛谷 P2014 [CTSC1997] 选课
  • ​NVIDIA V100、H100、A100 和 RTX 5090​​ 的显存
  • C++高性能日志库spdlog介绍
  • 【郑州课工场】深入解析Kubernetes 1.33版本Pod Priority and Preemption功能
  • 【免费版】开启 Youtube 双语字幕
  • C/C++---emplace和emplace_back
  • Go语言的包
  • TSN(时间敏感网络)协议栈在STM32平台(尤其是STM32MP2系列)上的实现
  • 设备虚拟化技术-IRF
  • C++ 中的默认构造函数:非必要,不提供
  • 苍穹外卖Day5
  • B树、B+树的区别及MySQL为何选择B+树
  • Git核心功能简要学习
  • GraphRAG快速入门和原理理解
  • 关于JVM
  • AXI接口学习
  • 上网行为管理-身份认证1
  • 剖析Sully.ai:革新医疗领域的AI助手功能启示
  • Hyperledger Fabric V2.5 生产环境部署及安装Java智能合约
  • 【OD机试】模拟数据序列号传输
  • 09_Spring Boot 整合 Freemarker 模板引擎的坑
  • 用简鹿视频格式转换器轻松制作 GIF 表情包教程
  • 牛客周赛 Round 101(题解的token计算, 76修地铁 ,76选数,76构造,qcjj寄快递,幂中幂plus)
  • 解决vscode中vue格式化后缩进太小的问题,并去除分号 - 设置Vetur tabSize从2到4,设置prettier取消分号semi