• 跳出Lambda表达式forEach()循环解决思路


    背景

    在一次需求开发时,发现使用Lambda的forEach()跳不出循环。如下示例代码,想在遍历满足条件时跳出循环

        public static void main(String[] args) {
            List<Integer> list = Arrays.asList(1, 4, 5, 7, 9, 11);
    
            list.forEach(e -> {
                if (e % 2 == 0) {
                    System.out.println("foreach -- " + e);
                    return;
                }
                System.out.println(e);
            });
    
            System.out.println(list);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行结果:
    在这里插入图片描述
    可以看出在forEach()中使用return并不会退出整个循环,和普通for循环return意义不同,仍会继续遍历。

    原因

    在普通for循环中,跳出循环使用break,结束本次循环使用continue,结束for循环所在的整个执行方法使用return。

          for (Integer e : list) {
                if (e % 2 == 0) {
                    break;  // return直接整个函数终止执行返回,break for循环方法终止执行
                }
                System.out.println(e);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Lambda表达式中,函数式接口Consumer 的抽象方法accept引用实现循环体中的逻辑。
    所以forEach()处理一个个的执行方法accept(t),非循环体。在执行方法中使用return将处理方法返回,但不能结束整个forEach()。

        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    @FunctionalInterface
    public interface Consumer<T> {
    
        void accept(T t);
    
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    优化方案

    跳出Lambda表达式forEach()循环解决思路有以下几种:

    • 抛异常
      在遍历时,若需要跳出循环,通过抛异常结束forEach()循环,在循环外catch异常不处理。首先此方案不够优雅,其次若循环逻辑块中有其他抛异常的地方,会受影响不易发现。
       public static void main(String[] args) {
            List<Integer> list = Arrays.asList(1, 4, 5, 7, 9, 11);
    
            try {
                list.forEach(e -> {
                    if (e % 2 == 0) {
                        System.out.println("foreach -- " + e);
                        throw new RuntimeException("跳出循环");
                    }
                    System.out.println(e);
                });
            } catch (Exception e) {}
    
            System.out.println(list);
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    执行结果:
    在这里插入图片描述

    • 使用普通for循环
      使用普通for循环替代Lambda表达式forEach()循环,在代码块中使用break即可跳出循环。

    • 使用Lambda表达式filter()实现
      换个实现思路,诉求是遍历list遇到第一个满足条件的item跳出循环,那么转成过滤整个list,返回第一个满足条件的item。

    Optional<Integer> first = list.stream().filter(e -> e % 2 == 0).findFirst();
    System.out.println(first.orElse(null));
    
    • 1
    • 2
    • 使用anyMatch()
      原理类似filter(),遇到满足条件的item跳出遍历返回。
          list.stream().anyMatch(e -> {
                if (e % 2 == 0) {
                    System.out.println("跳出循环 -- " + e);
                    return true;
                }
                System.out.println(e);
                return false;
            });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    执行结果:
    在这里插入图片描述

    总结

    Lambda表达式forEach()函数不支持return跳出循环,不建议使用抛异常方式结束循环,可以考虑使用普通for或利用Lambda表达式的函数实现。

  • 相关阅读:
    想要精通算法和SQL的成长之路 - K次取反后最大化的数组和
    【深度学习】实验6答案:图像自然语言描述生成(让计算机“看图说话”)
    微信小程序1(代码构成和基础组件和协同开发)
    服务器 jupyter 文件名乱码问题
    04 动力云客之登录后获取用户信息+JWT存进Redis+Filter验证Token + token续期
    postgresql创建只读权限的用户
    小程序如何设置各种时间参数
    SpringCloud与SpringBoot的版本对应
    马瑞利单点协议
    数值分析基础应用线性代数
  • 原文地址:https://blog.csdn.net/qq_38252499/article/details/132651082