Itera迭代
- [Iterable < T>迭代接口](#Iterable < T>迭代接口)
-
- [(1) Iterator iterator()](#(1) Iterator iterator())
- [(2) forEach(Consumer<? super T> action)](#(2) forEach(Consumer<? super T> action))
- [(3)Spliterator spliterator()](#(3)Spliterator spliterator())
- [Iterator< T>迭代器接口](#Iterator< T>迭代器接口)
- 总结
在 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)
表名这些集合类都是可以被迭代的,当然也都都可以使用迭代器