【问题标题】:How to make a generic for loop without knowing which endpoint is bigger?如何在不知道哪个端点更大的情况下制作通用 for 循环?
【发布时间】:2020-03-27 04:27:43
【问题描述】:

我的项目中到处都有以下代码,我想知道是否可以通过用一个递增的 for 循环替换它来用更少的代码行编写相同的代码或递减取决于endPosition 是大于还是小于startPosition。这可能吗?

if (endPosition > startPosition) {
    for (int i = startPosition; i <= endPosition; i++) {
        doStuff(i);
    }
} else {
    for (int i = startPosition; i >= endPosition; i--) {
        doStuff(i);
    }
}

Edit1:我已将“优化”一词更改为“编写更少的代码行”。另外,我添加了索引 i 作为 doStuff 的参数,以强调它的重要性。但是,访问每个元素的顺序不是。

【问题讨论】:

  • 顺序重要吗?还是只需要doStuff 一定次数?
  • 不需要,但 doStuff() 确实需要索引作为参数
  • 那么在您的示例代码中反映这一点可能是个好主意。
  • 我已经编辑了示例代码,感谢您的建议。

标签: java


【解决方案1】:

可以用三元条件运算符简化为单循环,但我觉得很丑:

boolean up = endPosition > startPosition;
for (int i = startPosition; up ? i <= endPosition : i >= endPosition; up ? i++ : i--) {
   doStuff();
}

当然,endPosition &gt; startPosition 条件可以内联以消除up 变量,但在我看来这会更难看。

【讨论】:

    【解决方案2】:
    int step = endPosition > startPosition ? 1 : -1;
    for (int i = startPosition ; (i-step) != endPosition; i += step) {
      doStuff(i);
    }
    

    请注意,这将始终执行 doStuff 至少一次(即 startPositionendPosition 被认为是包容性的)。

    【讨论】:

      【解决方案3】:

      您为此使用了“优化”一词,但您想要的不是优化。拥有更多更易于阅读和理解的代码总是比拥有更少的代码行更好,这会让你的团队成员挠头试图理解它。无论如何,就像一个谜一样,如果您想将代码保持在最低限度并且不使用任何if

      int max = (int)(((long)startPosition+endPosition) + Math.abs((long)startPosition - endPosition)) / 2;
      int min = (int)(((long)startPosition+endPosition) - Math.abs((long)startPosition - endPosition)) / 2;
      for (int i = min; i <= max; i++) {
          System.out.println(i);
      }
      

      这就是我如何做到这一点的。我开始了:

      int step = (endPosition - startPosition) / Math.abs(endPosition - startPosition);
      for (int i = startPosition; i != endPosition+step; i+=step) {
             System.out.println(i);
      }
      

      然后得到一个很好的评论:如果 endPosition=Integer.MIN_VALUE 和 startPosition>=0 怎么办?在这种情况下,我们希望 step=-1 但会发生(endPosition - startPosition) 不适合分配给 int 的大小。这意味着 int 的第一位,即给出数字符号的位,将被覆盖。因此,对于负值 -1,它不是我们期望的 1,而是 0。所以 step=1。为了解决这个问题,我会先转换为 long,然后再转换为 int:

      int step = (int)(((long)endPosition - startPosition) / Math.abs((long)endPosition - startPosition));

      还没有走出困境:如果 endPosition 等于 startPosition 我们得到除以零。所以...更好get the min and max mathematically。而且我们还想记住总和或差不适合 int 类型的情况。

      【讨论】:

      • 这会与endPosition = Integer.MIN_VALUEstartPosition &gt;= 0(或其他溢出情况)中断吗?
      【解决方案4】:

      如果你不需要在循环中使用索引,你可以将你的代码替换为next:

      int k = Math.abs(endPosition - startPosition);
      for (int i = 0; i <= k; i++) {
          doStuff();
      }
      

      【讨论】:

        【解决方案5】:

        您的问题与 cpu 优化无关。您要求的代码行数更少,这是不同的。

        如果顺序不重要,你可以这样做(但你的版本在 cpu 上更好):

        int[] limit = {Math.min(start, end), Math.max(start,end)};
        for (int i=limit[0]; i<limit[1]; i++) {
          doStuff();
        }
        

        如果顺序很重要,您可以使用任何其他答案...但无论如何,您的代码实际上在 cpu 上更好。

        【讨论】:

          【解决方案6】:

          可以循环使用它,但这是带有代码重复的实用代码。 lambda 是有意义的。

          walk(startPosition, endPosition, i -> { doStuff(); return false; });
          

          或与

          boolean doStuff2(int i) { return i == 42; }
          
          walk(startPosition, endPosition,  this::doStuff2);
          

          与:

          /**
           * @param step function that receives the index, and returns false to continue.
           * @return the index of a return true, otherwise -1.
           */
          int walk(IntPredicate step, int startPosition, int endPosition) {
              if (endPosition > startPosition) {
                  for (int i = startPosition; i <= endPosition; i++) {
                     if (step.test(i)) {
                        return i;
                     }
                  }
              } else {
                  for (int i = startPosition; i >= endPosition; i--) {
                     if (step.test(i)) {
                        return i;
                     }
                  }
              }
              return -1;
          }
          

          最干净:

          static IntStream walk(int startPosition, int endPosition) {
              return startPosition <= endPosition
                  ? IntStream.rangeClosed(startPosition, endPosition)
                  : IntStream.rangeClosed(-startPosition, -endPosition)
                          .map(i -> -i);
          }
          
              walk(1, 4).forEach(System.out::println);
              walk(8, 4).forEach(System.out::println);
          

          【讨论】:

            【解决方案7】:

            像这样:

            for(int i = Math.abs(endPosition - startPosition); i >= 0; i--) {
                doStuff();
            }
            

            【讨论】:

              【解决方案8】:

              如果性能是您主要关心的问题,那么您在问题中编写它的方式就是要走的路;但是,如果该模式在您的代码中多次出现,并且为了避免在循环中重复代码,您可以执行以下操作:

              public static void forEachInRange(int startPosition, int endPosition,
                      IntConsumer cons) {
                  if (endPosition > startPosition) {
                      for (int i = startPosition; i <= endPosition; i++) {
                         cons.accept(i);
                      }
                  } else {
                      for (int i = startPosition; i >= endPosition; i--) {
                         cons.accept(i);
                      }
                  }
              }
              

              【讨论】:

                【解决方案9】:

                我觉得这个问题很有趣,但我真的永远不会实现这样的东西,在极端情况下太容易出现无限循环......

                但只是为了好玩,您可以使用比较功能,并在没有任何条件的情况下实现代码。

                        int start ;
                        int step;
                        int end;
                
                        start = 3;
                        end = 9;
                        step = Integer.compare(end, start);
                
                        System.out.println("==========");
                        for(int i = start; i!=end+step;i+=step){
                            System.out.println(i);
                        }
                
                
                        end = 3;
                        start = 9;
                        step = Integer.compare(end, start);
                        System.out.println("==========");
                        for(int i = start; i!=end+step; i+=step){
                            System.out.println(i);
                        }
                
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-01-17
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-11-09
                  • 1970-01-01
                  • 2021-01-03
                  • 2023-03-05
                  相关资源
                  最近更新 更多