Guava 迭代器增强类介绍

作者:桦说编程日期:2025/11/10

Guava 库为 Java 开发者提供了一系列强大的迭代器增强工具,它们简化了复杂迭代模式的实现。本文将深入探讨 Guava 的 PeekingIterator、AbstractIterator 和 AbstractSequentialIterator。

1. PeekingIterator:洞察先机

标准的 Iterator 接口仅提供 hasNext() 和 next() 方法,这在某些场景下显得力不从心。当需要“预读”下一个元素以做出决策,但又不想立即消耗它时,PeekingIterator 应运而生。

核心功能:

  • Iterators.peekingIterator(Iterator) 方法用于将现有的 Iterator 包装成一个 PeekingIterator
  • 其核心方法是 peek(),允许在不推进迭代器的情况下,查看下一个调用 next() 时将返回的元素。

它带来的便利:

PeekingIterator 的 peek() 方法能够支持更灵活的迭代逻辑,尤其是在需要根据当前元素和“下一个”元素关系进行判断的场景中。它允许在一次迭代过程中完成复杂的逻辑判断和数据处理,避免了传统方法中可能出现的冗余 next() 调用或额外的状态变量管理。

示例:消除连续重复元素

1public class PeekingIteratorExample {
2    public static void main(String[] args) {
3        List<Integer> source = Arrays.asList(1, 1, 2, 3, 3, 3, 4, 5, 5);
4        List<Integer> result = Lists.newArrayList();
5
6        PeekingIterator<Integer> iter = Iterators.peekingIterator(source.iterator());
7
8        while (iter.hasNext()) {
9            Integer current = iter.next(); // 获取当前元素
10            result.add(current); // 将当前元素添加到结果
11
12            // 利用 peek() 提前判断,如果下一个元素与当前元素重复,则跳过。
13            while (iter.hasNext() && iter.peek().equals(current)) {
14                iter.next(); // 跳过这个重复元素
15            }
16        }
17        System.out.println("Original: " + source);
18        System.out.println("Deduplicated: " + result); // Output: [1, 2, 3, 4, 5]
19    }
20}
21

在此示例中,我们仅对 source 列表进行了一次逻辑遍历。PeekingIterator 使得我们能够在处理 current 元素的同时,预判并跳过所有后续的重复项,而无需将它们存储到任何中间结构或进行后续的去重处理。这简化了去重逻辑的实现。

注意事项:Iterators.peekingIterator 返回的 PeekingIterator 不支持在调用 peek() 之后执行 remove() 操作。

2. AbstractIterator:一个方法定义迭代器

当需要实现一个自定义的 Iterator,例如对数据进行过滤、转换或从复杂数据源中提取时,手动管理 hasNext()next() 的内部状态会引入大量样板代码。AbstractIterator 极大地简化了这一过程。

核心功能:

  • 开发者只需实现一个方法:computeNext()。此方法负责计算并返回序列中的下一个值。
  • 当迭代序列完成时,computeNext() 应返回 endOfData() 以标记迭代结束。

它带来的便利:

AbstractIterator 采用惰性求值(lazy evaluation)机制。它确保 computeNext() 方法仅在真正需要下一个元素(即 next() 被调用时)才会被执行。这使得迭代器能够按需生成元素,避免了在迭代器未被完全消费时进行不必要的计算。同时,Guava 负责处理 hasNext()next() 之间的状态同步,简化了自定义迭代器的实现。

示例:惰性过滤 Null 值

1public class AbstractIteratorExample {
2
3    public static Iterator<String> skipNulls(final Iterator<String> in) {
4        return new AbstractIterator<String>() {
5            @Override
6            protected String computeNext() {
7                while (in.hasNext()) {
8                    String s = in.next();
9                    if (s != null) {
10                        return s; // 找到非 null 元素,返回
11                    }
12                }
13                return endOfData(); // 内部迭代器耗尽,返回 endOfData()
14            }
15        };
16    }
17
18    public static void main(String[] args) {
19        List<String> source = Arrays.asList("hello", null, "world", null, "guava", "java");
20        Iterator<String> filteredIterator = skipNulls(source.iterator());
21
22        System.out.println("Filtered elements (only consuming first 3):");
23        int count = 0;
24        while (filteredIterator.hasNext() && count < 3) { // 假设我们只消费前3个元素
25            System.out.println(filteredIterator.next());
26            count++;
27        }
28        // Output:
29        // hello
30        // world
31        // guava
32    }
33}
34

在此示例中,即使 source 列表中有更多元素,我们只消费了前 3 个非空字符串。AbstractIterator 确保了 in.next()if (s != null) 这样的操作仅执行了刚好足以找到这 3 个元素所需的最小次数。这种按需计算的模式,使得迭代器只在需要时才处理数据。

限制: AbstractIterator 继承自 UnmodifiableIterator,这意味着它禁止实现 remove() 方法。如果您的迭代器必须支持 remove(),则不应继承 AbstractIterator。

``

3. AbstractSequentialIterator

对于那些下一个值很容易根据前一个值计算出来的迭代器,AbstractSequentialIterator 提供了一种表达迭代的替代方式。

核心功能:

  • 开发者实现的方法是 computeNext(T previous),它接受序列中的前一个值作为参数。
  • 必须在构造函数中提供一个初始值(或者如果迭代器应立即终止,则传入 null)。
  • computeNext() 方法约定,返回 null 意味着迭代结束。

它带来的便利:

AbstractSequentialIterator 适用于那些下一个值可以根据前一个值计算出来的序列。它通过**按需生成(on-demand generation)**机制,仅在 next() 被调用时才计算下一个元素。这使得它非常适合处理可能非常长甚至无限的序列,因为它避免了预先计算和存储整个序列。

示例:惰性计算 2 的幂次

1public class AbstractSequentialIteratorExample {
2    public static void main(String[] args) {
3        // 从1开始生成2的幂次方,直到达到 1 << 30
4        Iterator<Integer> powersOfTwo = new AbstractSequentialIterator<Integer>(1) {
5            @Override
6            protected Integer computeNext(Integer previous) {
7                // 如果 previous 已经达到最大值,则返回 null 结束迭代
8                return (previous == 1 << 30) ? null : previous * 2;
9            }
10        };
11
12        System.out.println("Powers of two (only consuming first 7):");
13        int count = 0;
14        while (powersOfTwo.hasNext() && count < 7) { // 假设我们只消费前7个元素
15            System.out.println(powersOfTwo.next());
16            count++;
17        }
18        // Output: 1, 2, 4, 8, 16, 32, 64
19    }
20}
21

在此示例中,我们仅消费了序列的前 7 个元素。AbstractSequentialIterator 确保了只有这 7 次乘法操作被执行,并且在内存中只维护了当前 previous 元素的状态。这种惰性生成方式,避免了对整个序列进行预计算和存储的需要。

关键限制: 由于返回 null 标志着迭代的结束,因此 AbstractSequentialIterator 不能用于实现一个可以合法返回 null 元素的迭代器。

笔者遇到一个按需深拷贝的需求,简单描述为:已获得对象a,需要深拷贝若干份,同时需要标记序号,a已经实现deepCopy方法。实现如下:

1// 实现1
2A a = getA();
3boolean first = true;
4for (var x: request.getXList()) {
5    // ...
6    if (a != null) {
7        if (first) {
8            x.setA(a);
9            first = false;
10        } else {
11            A newA = a.deepCopy();
12            newA.seq++;
13            x.setA(newA);
14        }
15    }
16    // ...
17}
18
19// 实现2,修改引用a
20A a = getA();
21for (var x: request.getXList()) {
22    // ...
23    if (a != null) {
24        x.setA(a);
25        a = a.deepCopy(); //会多复制一次
26        a.seq++;
27    }
28    // ...
29}
30
31// 新实现
32A a = getA();
33// copy iter
34Iterator<A> aIter = new AbstractSequentialIterator<>(a) {
35    @Override
36    protected A computeNext(A prev) {
37        A result = prev.deepCopy();
38        result.seq++;
39        return result;
40    }
41};
42for (var x: request.getXList()) {
43    // ...
44    if (a != null) {
45        x.setA(aIter.next());
46    }
47    // ...
48}
49

和原有实现相比,新实现抽取出了A的迭代逻辑,更好理解。

总结

Guava 的 PeekingIterator、AbstractIterator 和 AbstractSequentialIterator 为 Java 开发者提供了强大的工具,用于简化和增强迭代器的使用。它们通过以下方式提升代码的健壮性和可维护性:

  • PeekingIterator: 允许在单次遍历中进行预判和复杂逻辑处理,减少了状态管理和冗余操作。
  • AbstractIterator: 强制惰性求值,简化了自定义迭代器的实现,并按需处理数据。
  • AbstractSequentialIterator: 实现按需生成序列,适用于处理长序列,避免了不必要的预计算和存储。

在处理数据流、构建数据管道或实现复杂业务逻辑时,合理利用这些迭代器,可以使代码更加清晰、灵活,并更好地管理资源。


Guava 迭代器增强类介绍》 是转载文章,点击查看原文


相关推荐


🍎 Electron 桌面端应用合法性问题全流程解决指南(新手友好版)
去码头整点薯片2025/11/8

本文目标:帮助你把本地的 Electron 应用打包成 macOS 的 .dmg,并做到打开不再被 Gatekeeper 拦截(不再提示“来自身份不明的开发者/无法验证是否含有恶意软件”)。 适用对象:个人开发者 & 小团队。 🧩 一、问题场景 当你满心欢喜地将精心开发的 Electron 应用打包分发给用户,却接到反馈:在 macOS 上无法打开,系统弹窗冷冰冰地提示“无法验证开发者”,文件被直接移入废纸篓。 如果这个场景让你感同身受,那么你正遭遇 macOS 强大的 Gatekeepe


Python 的内置函数 globals
IMPYLH2025/11/6

Python 内建函数列表 > Python 的内置函数 globals Python 的内置函数 globals() 是一个非常重要的工具函数,它返回一个字典,表示当前全局符号表。这个字典包含了当前模块中定义的所有全局变量、函数和类的名称及其对应的值。 def globals(): ''' 返回实现当前模块命名空间的字典 :return: 当前模块命名空间的字典 ''' 具体来说: 返回值是一个字典对象字典的键是变量名或函数名(字符串形式)字典的值是


Python 的内置函数 filter
IMPYLH2025/11/1

Python 内建函数列表 > Python 的内置函数 eval Python 的内建函数 filter() 是一个非常有用的高阶函数,它用于对可迭代对象进行筛选过滤。它的基本工作原理是根据指定的函数条件,从输入的可迭代对象中筛选出符合条件的元素,返回一个迭代器对象。 def filter(fn, iterable): ''' 过滤数据 :param fn: 回调函数,返回 True 是元素保留,返回 False 时元素去除 :param iterable


彻底搞懂「字幕」:从格式、软硬到嵌入,告别所有困惑
mortimer2025/10/30

“我明明把字幕加上了,为什么播放时看不到?” “为什么别人的视频能切换中英文字幕,我的就不行?” “软字幕、硬字幕、.srt、.ass……这些到底是什么鬼?” 如果你曾被这些问题困扰,那么这篇文章就是为你准备的。我将用最清晰的逻辑和最易懂的语言,一次性讲透关于字幕的所有核心知识。 第一站:先懂最核心的区别 —— 软字幕 vs. 硬字幕 在讨论任何格式或工具之前,你必须先理解字幕最终呈现的两种形态。这决定了你的字幕能否被关闭或切换。 硬字幕 (Hard Subs):印在画面上的“纹身” 硬字幕


Redis(87)Redis缓存的LRU淘汰策略如何配置?
Victor3562025/10/27

配置Redis缓存的LRU(Least Recently Used,最近最少使用)淘汰策略需要以下几个步骤: 设置最大内存限制 选择和配置淘汰策略 验证配置 以下是详细的步骤和代码示例。 1. 设置最大内存限制 可以在Redis配置文件 redis.conf 中设置 maxmemory 选项来限制Redis使用的最大内存。 # redis.conf # 设置最大内存为256MB maxmemory 256mb 如果你不使用配置文件,可以通过命令行参数直接设置: redis-server -


Redis(80)如何解决Redis的缓存穿透问题?
Victor3562025/10/24

解决Redis缓存穿透问题的方法有多种,具体的解决方案可以根据实际情况选择。以下是几种常见的解决方法及详细的代码示例,包括缓存空结果、使用布隆过滤器以及参数校验。 1. 缓存空结果 当查询数据库返回空结果时,也将其缓存起来,并设置一个较短的过期时间,比如5分钟。这样即使请求数据不存在,也不需要每次都访问数据库。 示例代码: import redis.clients.jedis.Jedis; import com.fasterxml.jackson.databind.ObjectMapper;


零代码改造 + 全链路追踪!Spring AI 最新可观测性详细解读
阿里云云原生2025/10/22

作者:张铭辉(希铭) 本文部分总结于 8 月的 LoongSuite 系列公开课及 AI 原生应用实战营 meetup 分享内容。如需查看原视频欢迎移步:developer.aliyun.com/live/255218… 前言:AI Agent 从 Demo 到生产阶段的挑战 自 2022 年底 GPT-3.5 引爆大模型革命以来,AI 应用经历了从技术探索到产业落地的快速演进。开源模型迭代与低代码平台的兴起,推动了 AI Agent 开发效率的显著提升。然而,行业普遍面临一个核心矛盾:绝大多数


DeepSeek OCR:用'眼睛'阅读长文本,AI记忆新纪元?
墨风如雪2025/10/21

嘿,AI圈的朋友们!最近DeepSeek团队又搞了个大动作,发布了一款名叫DeepSeek-OCR的开源模型。但你可别以为这只是一个普通的文字识别工具,它的核心理念简直是脑洞大开,可能会彻底改变我们处理长文本的方式。 想象一下,我们的大语言模型(LLM)在面对海量文本时,常常会因为算力消耗过大而头疼不已,上下文一长,计算量就呈平方级增长。DeepSeek-OCR另辟蹊径,它不直接处理文本,而是巧妙地把文本信息“画”成图像,然后对这些图像进行压缩!是不是有点像人类先看图再理解,而不是一个字一个字地


如何从 iPhone 中导出视频
Digitally2025/10/20

如果你的 iPhone 存储空间不足,或者你想在发布到社交媒体之前编辑视频,你可以将视频从 iPhone 转移到电脑上,以释放更多空间。在 Windows 电脑上,通常使用 iTunes 在电脑和 iPhone 之间传输文件。在 Mac 上,如果你使用的是 macOS Mojave 或更早版本,可以使用 iTunes;如果你使用的是 macOS Catalina 或更高版本,则可以使用 Finder。在本文中,我们将向你展示多种从 iPhone 中导出视频的方法。有些方法需要使用 iTunes,


Pinia 状态管理原理与实战全解析
90后晨仔2025/10/19

一、前言:为什么选择 Pinia? 在 Vue2 时代,我们常用 Vuex 来做全局状态管理。 但是 Vue3 带来了全新的响应式系统(Composition API + Proxy),于是 Vue 官方团队推出了 Pinia —— 一款更轻量、更现代、更易用的状态管理库。 Pinia 的核心理念是: “让状态管理像使用普通变量一样简单。” 相比 Vuex,它具备以下优势: 特点VuexPinia语法基于 Mutation

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0