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)

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

相关推荐
Tirson Yang5 分钟前
西安java面试总结1
java·面试
小猫咪怎么会有坏心思呢6 分钟前
华为OD机试-猴子爬山-dp(JAVA 2025A卷)
java·算法·华为od
保持学习ing9 分钟前
SpringBoot 前后台交互 -- CRUD
java·spring boot·后端·ssm·项目实战·页面放行
啾啾Fun1 小时前
Java反射操作百倍性能优化
java·性能优化·反射·缓存思想
20岁30年经验的码农1 小时前
若依微服务Openfeign接口调用超时问题
java·微服务·架构
曲莫终1 小时前
SpEl表达式之强大的集合选择(Collection Selection)和集合投影(Collection Projection)
java·spring boot·spring
ajassi20002 小时前
开源 java android app 开发(十二)封库.aar
android·java·linux·开源
q567315232 小时前
Java使用Selenium反爬虫优化方案
java·开发语言·分布式·爬虫·selenium
kaikaile19952 小时前
解密Spring Boot:深入理解条件装配与条件注解
java·spring boot·spring
守护者1702 小时前
JAVA学习-练习试用Java实现“一个词频统计工具 :读取文本文件,统计并输出每个单词的频率”
java·学习