【问题标题】:How to create nice iterations in Java 8如何在 Java 8 中创建漂亮的迭代
【发布时间】:2015-03-02 23:50:05
【问题描述】:

Adam Bien's weblog 的启发,我想将 Java 7 中的常见迭代替换为 Java 8 中更好的迭代。旧代码如下所示:

void repeatUsingJava7(int times) {
    for (int i = 0; i < times; i++) {
        doStuff();
        doMoreStuff();
        doEvenMoreStuff();
    }
}

...这不太好。所以我用 Adam Bein 的例子把它替换成了这个:

void repeatUsingJava8(int times) {
    IntStream.range(0, times).forEach(
        i -> {
            doStuff();
            doMoreStuff();
            doEvenMoreStuff();
        }
    );
}

...这是朝着正确方向迈出的一步,但并没有使代码更易于阅读,并且还引入了一个不需要的变量i,以及一对额外的花括号。所以现在我想知道是否有其他方法可以编写此代码,这将使其更好、更易于阅读,主要使用 Java 8。

【问题讨论】:

  • 我看不出它们有何相似之处:我的问题是关于多次重复操作,而您所指的是关于迭代列表
  • 第二种表达代码的方式是不引入“一个不需要的变量i以及一对额外的大括号”。 两个代码片段都有一个变量i两个都有相同个大括号。嗯,它们等价的……

标签: java java-8 iteration


【解决方案1】:

我认为在这种情况下使用 Streams 没有任何优势。流对于处理包含多个相同类型元素的集合、数组和其他数据结构很有用。

在这里您不处理任何数据,您只是多次重复相同的操作。没有理由为此目的替换旧的 for 循环。

【讨论】:

  • 我认为这使代码更具表现力,我相信这就是 java 8 中的流和 lambdas 的全部意义所在。但我同意,我编写第二个示例的方式并没有使代码更简单,这就是为什么我要问是否有任何其他方式来编写它!
  • 一个常见的危险是每个人都想使用新的闪亮工具,即使旧的生锈工具也很好。当我看到 IDE 建议将集合上的普通 for 循环转换为 forEach 时,大多数情况下,这是错误的做法。在它们为您提供杠杆作用的地方使用新的闪亮工具。但是 for 循环并没有错。
【解决方案2】:

为了完整起见,这里有一个不需要计数器变量的解决方案:

void repeatUsingJava8(int times) {
    Collections.<Runnable>nCopies(times, ()->{
        doStuff();
        doMoreStuff();
        doEvenMoreStuff();
    }).forEach(Runnable::run);
}

如果只有一个方法可以被多个times调用,它会变得更具可读性,因为在这种情况下它可以写成,例如

void repeatUsingJava8(int times) {
    Collections.<Runnable>nCopies(times, this::doStuff).forEach(Runnable::run);
}

如果一定要Streams,上面的代码相当于

void repeatUsingJava8(int times) {
    Stream.<Runnable>generate(()->this::doStuff).limit(times).forEach(Runnable::run);
}

但是,这些替代方案并不比旧的 for 循环更好。如果您考虑parallel 执行,这是Streams 相对于普通for 循环的真正优势,那么仍然存在基于众所周知的、已批准的API 的替代方案:

ExecutorService es=Executors.newCachedThreadPool();
es.invokeAll(Collections.nCopies(times, Executors.callable(()->{
    doStuff();
    doMoreStuff();
    doEvenMoreStuff();
})));

【讨论】:

    【解决方案3】:

    正如其他人指出的那样,对于这个特定的示例,用流替换简单的 for 循环不一定更好。但是,如果您要解决的问题更普遍,优势就会开始显现。例如,假设您需要重复一些作为参数传递的代码,而不是重复某些特定代码 n 次?考虑:

    void repeat(int count, Runnable action) {
        IntStream.range(0, count).forEach(i -> action.run());
    }
    

    现在你可以写了,

    repeat(3, () -> System.out.println("Hello!"));
    

    或许

    repeat(4, this::doStuff);
    

    您可能希望执行 多个 次操作 n 次,而不是执行单个操作 n 次。你可以这样做:

    void repeat(int count, Runnable... actions) {
        IntStream.range(0, count).forEach(i -> Arrays.asList(actions).forEach(Runnable::run));
    }
    

    那么你就可以写了:

    repeat(5, this::doStuff, this::doMoreStuff, this::doEvenMoreStuff);
    

    repeat 实现比传统的 Java 7 方式更简洁(也许更简洁):

    void repeatOldWay(int count, Runnable...actions) {
        for (int i = 0; i < count; i++) {
            for (Runnable r : actions) {
                r.run();
            }
        }
    }
    

    真正的优势在于调用站点,您可以简单地传入一个计数和一个或多个 lambda 表达式或方法引用,而不是复制嵌套的 for 循环逻辑。

    【讨论】:

      【解决方案4】:

      你也可以使用from谓词:

      IntStream.range(0, 10).sorted().filter(i -> i > 5).forEach(System.out::println);
      

      排序和过滤器是附加的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-09
        • 2013-05-20
        • 2013-05-25
        • 1970-01-01
        • 2015-09-14
        • 2014-12-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多