Java中可通过封装原始迭代器并重写next()和hasNext()实现过滤迭代器,它延迟查找满足Predicate条件的元素,避免预加载;核心是缓存首个匹配项并在next()后清空,支持Iterable封装以用于for-each循环。

在 Java 中,可以通过封装原始迭代器并重写 next() 和 hasNext() 方法,实现一个带过滤逻辑的自定义迭代器——即“过滤迭代器”(Filtering Iterator),它在遍历时自动跳过不满足条件的元素。
核心思路:包装原始迭代器 + 延迟查找下一个有效元素
过滤迭代器不预先收集所有匹配元素(避免内存浪费和提前计算),而是在每次调用 hasNext() 时,向内层迭代器不断推进,直到找到第一个满足条件的元素,并缓存它;next() 则返回该缓存项,并清空缓存以触发下一次查找。
- 构造时接收一个
Iterator<T>和一个Predicate<T>过滤条件 -
hasNext()负责“找下一个”,内部循环调用内层next()直到满足条件或耗尽 -
next()返回已找到的元素,并将缓存置为null,确保下次hasNext()重新查找
手写一个泛型过滤迭代器类
以下是一个简洁、线程不安全但实用的实现:
public class FilteringIterator<T> implements Iterator<T> {
private final Iterator<T> source;
private final Predicate<T> predicate;
private T nextMatch = null;
private boolean hasNextChecked = false;
public FilteringIterator(Iterator<T> source, Predicate<T> predicate) {
this.source = source;
this.predicate = predicate;
}
@Override
public boolean hasNext() {
if (hasNextChecked) return nextMatch != null;
hasNextChecked = true;
while (source.hasNext()) {
T item = source.next();
if (predicate.test(item)) {
nextMatch = item;
return true;
}
}
nextMatch = null;
return false;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
T result = nextMatch;
nextMatch = null;
hasNextChecked = false;
return result;
}
}
使用示例:跳过字符串长度小于 3 的元素
立即学习“Java免费学习笔记(深入)”;
List<String> list = Arrays.asList("a", "bb", "ccc", "dd", "eee");
Iterator<String> filtered = new FilteringIterator<>(list.iterator(), s -> s.length() >= 3);
while (filtered.hasNext()) {
System.out.println(filtered.next()); // 输出 "ccc", "eee"
}
结合 Iterable 封装成更易用的接口
为提升可用性,可提供一个静态工厂方法,返回 Iterable<T>,方便用于 for-each 循环:
public class Iterators {
public static <T> Iterable<T> filter(Iterator<T> source, Predicate<T> predicate) {
return () -> new FilteringIterator<>(source, predicate);
}
// 也可重载支持 Collection 或 Stream
}
这样就能写成:
for (String s : Iterators.filter(list.iterator(), s -> s.length() >= 3)) {
System.out.println(s); // 同样输出 "ccc", "eee"
}
注意事项与替代方案
Java 8+ 更推荐优先使用 Stream.filter(),语义清晰且组合性强:
list.stream()
.filter(s -> s.length() >= 3)
.forEach(System.out::println);
但注意:Stream 是一次性消费的,不能重复遍历;而自定义迭代器可多次使用(只要底层数据未变)。若需复用、低内存占用或集成到已有迭代器链中(如装饰器模式),手写过滤迭代器仍是必要选择。


















