Guava 迭代器增强类介绍

2025-12-04 0 942

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

1. AbstractSequentialIterator

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

核心功能:

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

它带来的便利:

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

示例:惰性计算 2 的幂次

public class AbstractSequentialIteratorExample {
    public static void main(String[] args) {
        // 从1开始生成2的幂次方,直到达到 1 << 30
        Iterator powersOfTwo = new AbstractSequentialIterator(1) {
            @Override
            protected Integer computeNext(Integer previous) {
                // 如果 previous 已经达到最大值,则返回 null 结束迭代
                return (previous == 1 << 30) ? null : previous * 2;
            }
        };

        System.out.println(\"Powers of two (only consuming first 7):\");
        int count = 0;
        while (powersOfTwo.hasNext() && count < 7) { // 假设我们只消费前7个元素
            System.out.println(powersOfTwo.next());
            count++;
        }
        // Output: 1, 2, 4, 8, 16, 32, 64
    }
}

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

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

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

// 实现1
A a = getA();
boolean first = true;
for (var x: request.getXList()) {
    // ...
    if (a != null) {
        if (first) {
            x.setA(a);
            first = false;
        } else {
            A newA = a.deepCopy();
            newA.seq++;
            x.setA(newA);
        }
    }
    // ...
}

// 实现2,修改引用a
A a = getA();
for (var x: request.getXList()) {
    // ...
    if (a != null) {
        x.setA(a);
        a = a.deepCopy(); //会多复制一次
        a.seq++;
    }
    // ...
}

// 新实现
A a = getA();
// copy iter
Iterator aIter = new AbstractSequentialIterator(a) {
    @Override
    protected A computeNext(A prev) {
        A result = prev.deepCopy();
        result.seq++;
        return result;
    }
};
for (var x: request.getXList()) {
    // ...
    if (a != null) {
        x.setA(aIter.next());
    }
    // ...
}

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

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

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

核心功能:

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

它带来的便利:

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

示例:惰性过滤 Null 值

public class AbstractIteratorExample {

    public static Iterator skipNulls(final Iterator in) {
        return new AbstractIterator() {
            @Override
            protected String computeNext() {
                while (in.hasNext()) {
                    String s = in.next();
                    if (s != null) {
                        return s; // 找到非 null 元素,返回
                    }
                }
                return endOfData(); // 内部迭代器耗尽,返回 endOfData()
            }
        };
    }

    public static void main(String[] args) {
        List source = Arrays.asList(\"hello\", null, \"world\", null, \"guava\", \"java\");
        Iterator filteredIterator = skipNulls(source.iterator());

        System.out.println(\"Filtered elements (only consuming first 3):\");
        int count = 0;
        while (filteredIterator.hasNext() && count < 3) { // 假设我们只消费前3个元素
            System.out.println(filteredIterator.next());
            count++;
        }
        // Output:
        // hello
        // world
        // guava
    }
}

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

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

3. PeekingIterator:洞察先机

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

核心功能:

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

它带来的便利:

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

示例:消除连续重复元素

public class PeekingIteratorExample {
    public static void main(String[] args) {
        List source = Arrays.asList(1, 1, 2, 3, 3, 3, 4, 5, 5);
        List result = Lists.newArrayList();

        PeekingIterator iter = Iterators.peekingIterator(source.iterator());

        while (iter.hasNext()) {
            Integer current = iter.next(); // 获取当前元素
            result.add(current); // 将当前元素添加到结果

            // 利用 peek() 提前判断,如果下一个元素与当前元素重复,则跳过。
            while (iter.hasNext() && iter.peek().equals(current)) {
                iter.next(); // 跳过这个重复元素
            }
        }
        System.out.println(\"Original: \" + source);
        System.out.println(\"Deduplicated: \" + result); // Output: [1, 2, 3, 4, 5]
    }
}

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

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

总结

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

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

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

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 Guava 迭代器增强类介绍 https://www.zuozi.net/3595.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务