java基础 迭代Iterable接口以及迭代器Iterator

Itera迭代

在 Java 中, Iterable < T> 是一个核心接口,用于表示一个对象可以被 迭代(遍历) ,它定义了如何通过 迭代器(Iterator< T>)访问 其元素,并支持增强的 for-each 循环

Iterable < T>迭代接口

java 复制代码
public interface Iterable<T> {
    Iterator<T> iterator(); // 必须实现的方法

    // Java 8 新增的默认方法
    default void forEach(Consumer<? super T> action) { /* ... */ }

    // Java 8 新增的默认方法
    default Spliterator<T> spliterator() { /* ... */ }
}

(1) Iterator iterator()

作用:返回一个迭代器(Iterator< T>) ,用于遍历集合 中的元素或进行删除元素 操作。

实现要求:每个 Iterable 实现类必须提供该方法,返回一个新的迭代器实例

(2) forEach(Consumer<? super T> action)

作用:对集合中的每个非空元素进行遍历

java 复制代码
	// 下界通配符 <? super T> 表示"T 或 T 的父类"
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

首先,这段代码里的forEach方法接受一个Consumer(消费者)参数,Consumer是一个函数式接口,接受一个输入参数并且不返回结果,accept方法为void

其次,这里用了泛型通配符<? super T>,表示Consumer可以处理T类型及其父类,比如有一个Animal的Consumer,而T是Dog,那么也可以接受这个Consumer。

步骤1.首先Objects.requireNonNull,确保传入的action不是null,避免空指针异常,注意是action判空

步骤2.接下来是一个增强的for循环,遍历this,也就是当前的Iterable实例

步骤3.每次循环取出元素t,调用action的accept方法处理它,具体如何处理 t,完全由 Consumer 的实现逻辑决定,是具体的消费操作action

forEach结合Consumer常见场景

(1) 示例 1:打印元素

java 复制代码
List<String> list = Arrays.asList("A", "B", "C");
list.forEach(element -> System.out.println(element));

forEach方法参数是 str -> System.out.println(str) :是一个 Consumer< String> 的实现,action就是打印元素:每次调用 accept(t) 时,会执行 System.out.println(t),即打印当前元素。

(2) 示例 2:修改对象状态

java 复制代码
List<Person> persons = getPersons();
persons.forEach(person -> person.setAge(18)); // 将所有 Person 的年龄设为 18

逻辑分解:

person -> person.setAge(18) 是 Consumer 的实现。每次调用 accept(t) 时,执行 t.setAge(18),修改元素的状态。

(3) 示例 3:条件判断与过滤

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
numbers.forEach(num -> {
    if (num % 2 == 0) {
        System.out.println(num + " 是偶数");
    }
});

在 accept(t) 中,根据 t 的值执行条件判断,仅处理满足条件的元素。

(4) 示例 4:方法引用

java 复制代码
list.forEach(System.out::println); // 方法引用

等价于:

java 复制代码
list.forEach(str -> System.out.println(str));

原理:System.out::println 是 Consumer< String> 的实现,accept(t) 会调用 System.out.println(t)

forEach使用注意细节

①上述的判空处理是判断集合是否或操作是否为空,不是判断集合中的某个元素为null

②对于Null的元素,foreach遍历输出要看具体的子类处理,如List的遍历是将其作为 null字符打印处理

java 复制代码
        List<String> list = Arrays.asList("A",null, "C");
        list.forEach(element -> System.out.println(element));

输出

java 复制代码
A
null //字符null 不是空 Null
C

(3)Spliterator spliterator()

Spliterator可拆分迭代器介绍链接
流Stream AP介绍点击链接

Spliterator 是 Stream API 并行处理的底层实现,现在的实际工作场景中,应用流对集合进行处理是 非常常见的,代码使用是非常简洁的,这两个单独拿出来说

Iterator< T>迭代器接口

Iterator 是 Java 集合框架中用于遍历集合元素的接口,定义如下:

java 复制代码
public interface Iterator<T> {
    boolean hasNext();    // 检查是否还有下一个元素
    T next();             // 返回下一个元素
    void remove();        // 移除当前元素(可选操作)
}

如何"接收" Iterator

(1) 通过集合的 iterator() 方法

有实现了 Iterable 接口的集合类(如 List, Set, Queue)都可通过 iterator() 方法返回迭代器:

java 复制代码
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iterator = list.iterator(); // 获取迭代器

(2) 自定义集合类实现 Iterable<T>

若需让自定义集合支持迭代,需实现 Iterable 并返回自定义的 Iterator

java 复制代码
public class MyCollection<T> implements Iterable<T> {
    private List<T> elements = new ArrayList<>();

    public void add(T element) {
        elements.add(element);
    }

    @Override
    public Iterator<T> iterator() {
        return new MyIterator(); // 返回自定义迭代器
    }

    // 自定义迭代器实现
    private class MyIterator implements Iterator<T> {
        private int index = 0;

        @Override
        public boolean hasNext() {
            return index < elements.size();
        }

        @Override
        public T next() {
            if (!hasNext()) throw new NoSuchElementException();
            return elements.get(index++);
        }

        @Override
        public void remove() {
            elements.remove(--index);
        }
    }
}

核心方法

(1) boolean hasNext()

作用:判断集合中是否还有未被遍历的元素。

返回值:

true:存在下一个元素。

false:已遍历完所有元素。

java 复制代码
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> it = list.iterator();
while (it.hasNext()) { // 检查是否还有元素
    String element = it.next();
    System.out.println(element);
}

(2) T next()

作用:返回当前指向的元素,并将迭代器位置后移。

注意事项:

调用前必须检查 hasNext(),否则可能抛出 NoSuchElementException

返回类型为泛型 T,确保类型安全

java 复制代码
Iterator<Integer> it = List.of(1, 2, 3).iterator();
if (it.hasNext()) {
    int num = it.next(); // 返回 1,迭代器指向下一个元素
}

(3) void remove()

作用:移除迭代器最后一次通过 next() 返回的元素。

注意事项:

必须在 next() 之后调用,否则抛出 IllegalStateException

不是所有迭代器都支持此操作,如ArrayList的迭代器执行此操作,不支持删除操作的迭代器如Collections.unmodifiableList()返回的迭代器,调用前需检查是否实现(默认可能抛出 UnsupportedOperationException)。

java 复制代码
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]

(4) forEachRemaining(Consumer<? super E> action)

上面三个方法是迭代器的核心方法,这个方法是将上面三个方法集合在一起了,他是对父接口的for-each方法的一种补充

java 复制代码
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }

和for-each区别就在于,for-each会将集合中的全部元素按照执行操作执行一遍,forEachRemaining则是将剩余未处理的元素按照执行操作执行一遍,配合可拆分迭代器spliterator中使用样例

java 复制代码
List<String> list = Arrays.asList("Java", "Python", "C++", "Go");
Spliterator<String> spliterator = list.spliterator();

// 处理前两个元素
spliterator.tryAdvance(System.out::println); // Java
spliterator.tryAdvance(System.out::println); // Python

// 这时候还剩余两个元素 批量处理剩余元素 
spliterator.forEachRemaining(System.out::println); 

// 输出:
// C++
// Go

迭代器的使用场景

(1) 遍历集合并修改元素

java 复制代码
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    int num = it.next();
    if (num % 2 == 0) {
        it.remove(); // 安全删除偶数
    }
}

(2) 遍历不可索引的集合(如 Set)

java 复制代码
Set<String> names = new HashSet<>(Set.of("Alice", "Bob"));
Iterator<String> it = names.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

(3) 结合流式处理(Stream API)

这种方式也是实际工作中常见的

java 复制代码
List<String> list = List.of("Java", "Python", "C++");
list.stream()
    .filter(s -> s.startsWith("J"))
    .forEach(System.out::println); // 输出 "Java"

注意事项

①迭代器的三个方法执行有先后顺序

boolean hasNext(); // 检查是否还有下一个元素

T next(); // 返回下一个元素

void remove(); // 移除当前元素(可选操作)
hasNext() 先保证还有下一个元素 ===》next()指针才做指向下一个元素返回---》指向返回元素才能进行操作元素 remove();删除

②单次遍历 :一个迭代器只能遍历集合一次,遍历结束后需重新获取迭代器。不可重复使用迭代器 即每次调用 iterator() 应返回一个新的迭代器实例。

③并发修改

java 复制代码
List<String> list = new ArrayList<>(Arrays.asList("A", "B"));
Iterator<String> it = list.iterator();
list.add("C"); // 直接修改集合
it.next();     // 抛出 ConcurrentModificationException

解决方案:使用并发集合 (如 CopyOnWriteArrayList)或在迭代期间仅通过迭代器修改集合

总结

1.迭代Iterable接口和迭代器Iterator接口 联系与区别
①关联:Iterable接口主要生成迭代器和增强for each循环,而增强的 for-each 循环底层依赖 Iterator,无论是在集合还是数组上使用 for-each 循环,其内部都会转换为相应的迭代器操作,具体的实现方式需要去查看迭代接口的实现类,如ArrayList

java 复制代码
//增强for each循环
for (String s : list) {
    System.out.println(s);
}
//等价于:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    System.out.println(s);
}

②区别:除此之外迭代器提供了一种更为安全的删除元素的操作方式,即迭代器的三个关键方法:hasNext(), next(), remove()配合使用,当仅仅需要遍历集合使用for-each就可以,但如果需要删除元素时,可以使用迭代器的 remove() 方法,两者的主要区别

Iterable<T> Iterator<T>
表示对象可被迭代 实际执行迭代操作
定义 iterator() 方法 定义 hasNext(), next(), remove()
支持 for-each 循环 手动控制遍历过程

2.集合框架中几乎所有集合类都实现了 Iterable,例如

  • List(如 ArrayList, LinkedList)
  • Set(如 HashSet, TreeSet)
  • Queue(如 PriorityQueue)

表名这些集合类都是可以被迭代的,当然也都都可以使用迭代器

相关推荐
无名之逆1 分钟前
高性能文件上传服务
java·服务器·网络·http·rust
Ray-国23 分钟前
2025蓝桥杯JavaB组
java·职场和发展·蓝桥杯
KEEPMA33 分钟前
在线上定位1G日志文件中的异常信息时,我这样做合适吗
java·服务器·数据库
一只小闪闪1 小时前
langchain4j搭建失物招领系统(六)---实现失物查询功能-RAG使用
java·人工智能·后端
雷渊1 小时前
RocketMQ生产者的消息被消费后会永久放在磁盘里吗?
java·后端·面试
2401_890666131 小时前
免费送源码:Java+ssm+MySQL 校园二手书销售平台设计与实现 计算机毕业设计原创定制
java·spring boot·python·mysql·小程序·php·课程设计
言小乔.1 小时前
202527 | RabbitMQ-基础 | 队列 | Direct + Fanout + Topic 交换机 | 消息转换器
java·微服务·消息队列·rabbitmq·mq·消息中间件
SoFlu软件机器人1 小时前
高并发场景下的 Java 性能优化
java·开发语言·性能优化
今天不学习明天变拉吉2 小时前
分页查询列表每页1000条的优化
java·数据库·mysql·性能优化
green5+12 小时前
卡码网55:右旋字符串
java·开发语言