V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
BIGBIG
V2EX  ›  Java

如何优雅的跳出多层循环?

  •  1
     
  •   BIGBIG · 178 天前 · 3193 次点击
    这是一个创建于 178 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如何优雅的跳出多层循环?

    现有一个数据清洗任务。有多层循环,如下伪代码。 我的问题是:当 cleanedCount 达到 1 百万条数据时,要停止整个清洗任务。难道要一层一层 return 出去么?

    class CleanData{
    
      // 已清洗数据量
      cleanedCount ;
    
      // 迁移任务
      method cleanByTask() {
        activityIdList.foreach{activityId -> cleanByActivity(activityId)}
      }
    
      // 按活动
      method cleanByActivity(activityId) {
        tables.foreach{table -> cleanByTable(table)}
      }
    
      // 按表
      method cleanByTable(table) {
        timeSplits.foreach{timeSplit -> cleanByTimeSplit(timeSplit)}
      }
      
      // 按时间切片
      method cleanByTimeSplit(timeSplit) {
        每 500 条.foreach{500 条 -> cleanByCount(500 条)}
      }
    
      // 按数量
      method cleanByCount(500 条) {
        cleanedCount += 500
        if (cleanedCount >= 1000000) {
          reurn;
        }
      }
    
    }
    
    32 条回复    2024-03-14 14:28:00 +08:00
    sockpuppet9527
        1
    sockpuppet9527  
       178 天前
    本身,引入一个状态机?或在 caller 那边做个状态?
    chendy
        2
    chendy  
       178 天前
    代码目测没啥问题
    一层一层 reture 也没啥问题
    难道楼主需要 System.exit(0) ?
    elliottzhao87
        3
    elliottzhao87  
       178 天前
    在外部放一个变量,检测变量直接在外层跳出呗?
    xausky
        4
    xausky  
       178 天前   ❤️ 1
    抛出个 Runtime 异常,最外层捕获处理,当然这个是旁门左道,正常还是多层 return 吧。
    xwayway
        5
    xwayway  
       178 天前   ❤️ 15
    当然是 goto 啦,想去哪儿去哪儿
    zhlxsh
        6
    zhlxsh  
       178 天前 via iPhone   ❤️ 1
    @xwayway 蒙多哈,想去哪就去哪
    virusdefender
        7
    virusdefender  
       178 天前   ❤️ 1
    抛出特定的异常
    darkengine
        8
    darkengine  
       178 天前
    Java 的不知道啊。这个逻辑在 JS/TS 有问题,即使总数大于 1000000 也只是退出一个 foreach 代码块,没跑完的还是会空跑。
    broken123
        9
    broken123  
       178 天前
    @zhlxsh 就你最优秀
    broken123
        10
    broken123  
       178 天前
    直接问 chatgpt 即可
    lsk569937453
        11
    lsk569937453  
       178 天前
    用 stream.flatMap.limit 完美解决
    darkengine
        12
    darkengine  
       178 天前
    而且 foreach 空跑还会进入 cleanByCount ,现在的代码会导致 cleanedCount 不准确

    method cleanByCount(500 条) {
    cleanedCount += 500
    if (cleanedCount >= 1000000) {
    reurn;
    }
    }

    改成
    method cleanByCount(500 条) {
    if (cleanedCount >= 1000000) {
    reurn;
    }
    cleanedCount += 500
    }
    pengtdyd
        13
    pengtdyd  
       178 天前
    那当然是 kill -9 啦
    cailinunix
        14
    cailinunix  
       178 天前
    尝试扁平化你的数据,把多层循环拍平成一个迭代器,然后就可以随便跳出了
    Aresxue
        15
    Aresxue  
       178 天前
    所以说 goto 部分场景下还是有价值的。
    针对这个场景用流也是个不错的方案。
    nthin0
        16
    nthin0  
       178 天前   ❤️ 1
    我选择抛特定异常。。一层层 return 要加的判断比较多,有点丑陋
    williamx
        17
    williamx  
       178 天前   ❤️ 1
    绝大多数情况下,你这个是伪需求。每一层都有退出条件,退出时需要收尾,所以最内层退出后会自动一层层退出。

    如果代码写得有漏洞 / 某些层条件没有配置好不能修改或者不想修改 / 想走特殊的逻辑而不是原来正常的逻辑,那就是异常情况,使用异常处理。
    yazinnnn0
        18
    yazinnnn0  
       178 天前   ❤️ 1
    java 有 goto 关键字, 但是这个关键字没有作用

    有 break label 的语法, 但是你拆成多个函数就没办法用了

    还是抛异常吧
    GeruzoniAnsasu
        19
    GeruzoniAnsasu  
       178 天前   ❤️ 1
    #7 +1:

    method exceptionCaptured() {
    try {cleanByTask()} catch(CountLimitExceeded){}
    }

    #4
    > 抛出个 Runtime 异常,最外层捕获处理,当然这个是旁门左道,正常还是多层 return 吧。

    不是旁门左道,这种不就是 recoverable exceptions, 抛异常是对的
    Leviathann
        20
    Leviathann  
       178 天前
    用 monad 的 flatMap
    nodejsexpress
        21
    nodejsexpress  
       178 天前
    不用 for, 用 while, 多几个条件 and 一起就好了.
    adoal
        22
    adoal  
       178 天前
    惰性求值,摊平成迭代器
    kyuuseiryuu
        23
    kyuuseiryuu  
       178 天前 via iPhone
    do
    for each
    if something
    break
    while false
    BIGBIG
        24
    BIGBIG  
    OP
       178 天前
    结案啦:1. 抛出指定异常; 2. 然后捕获异常。优雅永不过时
    感谢各位巨佬。


    帖子下沉啦
    iosyyy
        25
    iosyyy  
       178 天前
    尽量减少循环
    baoshijiagong
        26
    baoshijiagong  
       178 天前   ❤️ 1
    要看情况,如果“cleanedCount 达到 1 百万条数据” 是异常情况,那么用抛异常,属优雅;如果是正常流程,那么只是形式上的优雅,逻辑上不算。

    后者可以用方法函数,将多层的 cleanByXXX 的实现抽象成同一个抽象方法,在这个抽象方法判断 cleanedCount 即可。

    新建抽象方法:

    public <P, C> void clean(P parentId, Function<P, List<C>> getChildList, Consumer<C> cleanChild) {
    if (cleanedCount < 10_000_000) {
    List<C> childList = getChildList.apply(parentId);
    childList.forEach(cleanChild);
    }
    }

    比如 cleanByTask 改成:

    public void cleanByTask() {
    clean(null, p -> {
    // getActivityIdList
    return new ArrayList<>();
    }, this::cleanByActivity);
    }
    gg1025
        27
    gg1025  
       178 天前
    别问,问就是 goto
    KaGaMiKun
        28
    KaGaMiKun  
       178 天前
    第一反应是实现迭代器
    但这迭代器使用场景很少,仅仅可能为了这处省个返回才写的,顿时感觉还是不如老实返回
    billccn
        29
    billccn  
       177 天前   ❤️ 1
    抛异常就是正解,一些函数式编程语言(比如 OCaml )抛异常是从内层退出循环的唯一的方式,这些语言是研究计编程理论的人喜欢用的,他们都没觉得是抛异常是旁门左道。

    有一些老程序员会说尽量不要抛异常,是因为收集堆栈信息会比较慢,但是很多编程语言现在都对异常做了优化。比如 Java 虚拟机能识别这种用于控制程序执行,而不是用于报告错误的异常,抛出这种异常的时候里面就不会有堆栈信息,与创建一个普通对象无异。
    mmdsun
        30
    mmdsun  
       176 天前   ❤️ 1
    fillInStackTrace 抛异常,对性能影响很小。

    public final class StopException extends RuntimeException {
    public static final StopException INSTANCE = new StopException();

    @Override
    public synchronized Throwable fillInStackTrace() {
    return this;
    }
    }
    leee41
        31
    leee41  
       44 天前
    goto label
    你看反编译出来的 class 就能经常看到了,这个真有用,你这个场景完美 cover
    leee41
        32
    leee41  
       44 天前
    代码补充
    ```
    outer: // 这是一个标签
    for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
    if (i * j > 10) {
    System.out.println("Breaking from nested loop");
    break outer; // 跳出标签指定的循环
    }
    }
    }
    System.out.println("Exited loop");
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2932 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 13:04 · PVG 21:04 · LAX 06:04 · JFK 09:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.