首页 文章

在Lambda或简单循环方面哪个更好? [重复]

提问于
浏览
38

这个问题在这里已有答案:

我已经快速阅读了Oracle Lambda Expression文档 .

这种例子帮助我更好地理解了:

//Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
System.out.println(n);
}

//New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));


//or we can use :: double colon operator in Java 8
list.forEach(System.out::println);

不过,我不明白为什么会有这样的创新 . 它只是一种在“方法变量”结束时死亡的方法,对吗?为什么我应该使用它而不是真正的方法?在性能方面哪个是更好的选择 . Lambda或简单的循环 .

9 回答

  • 0

    运行时和可读性当然看起来有所不同,但在我看来,有一个很好的答案

    为什么我应该使用它而不是真正的方法?

    You can pass a lambda function as a variable
    如果我现在已成为Fantom用户一段时间,请纠正我,但您可以将Lambda函数作为参数传递给方法调用 . 例如,如果您构建了一个sort方法,则可以将lambda参数作为比较器使用,允许您使用单一排序方法轻松比较对象中的不同字段,甚至是完全不同的对象 .

  • 40

    我的建议是:

    • 使用您和您的同事认为最易维护的风格,

    • 如果你/你的同事还不熟悉lambdas,继续学习

    • 不要过于痴迷于表现 .

    一般来说,lambdas和stream提供了更简洁的(一旦每个人都达到速度)更可读的表达这种算法的方式 . 性能不是主要目标 .

    如果性能确实成为问题,那么标准建议就是编码,测试,分析和优化 . 您可以通过在编码阶段进行优化来轻松浪费时间 . 让分析器指出代码的哪些部分值得优化 .


    在这个具体的例子中,性能差异太小而无法衡量 . 如果你扩展到数百万个元素的列表,性能将由构建列表和写入数字所花费的时间占主导地位 . 不同的迭代方式只会对整体性能产生很小的影响 .


    对于那些人(尽管如上所述)仍然想知道使用lambda或传统循环是否更快,最好的一般答案是:

    “这取决于各种因素1)未被充分理解,2)随着Java编译器技术的发展而易于改变 .

    我们可以为特定Java主要/次要/补丁版本的特定示例提供答案,但是概括是不明智的 .

  • 0

    为什么我应该使用它而不是真正的方法?

    你不应该 . 使用您更喜欢的方法 .

    至于性能,我想,所有这些版本大致同样快 . 这里I / O操作( println )比调用lambda或创建迭代器的所有可能开销要慢得多 . 通常 forEach 可能稍快一些,因为它在单个方法中执行所有内容而不创建 Iterator 并调用 hasNextnext (这是由for-each循环隐式完成的) . 无论如何,这取决于许多因素,例如您调用此代码的频率,列表的长度,JIT编译器是否设法对迭代器和/或lambda进行虚拟化等等 .

  • 2

    它允许你在一行中写入(虽然具有足够的可读性),这是以前不可能的 . 性能不是问题 O(n) 保持 O(n) .

    例如,在我的Windows Phone应用程序中,我有会话,并且该会话是表演者,我想选择所有具有一个具体表演者的会话(就像你想看到一些演员所播放的所有电影) . 在Java 1.7或更低版本中,我必须使用内部循环创建循环,检查它,如果没有执行者则返回null等 . 使用lambda表达式,我可以这样做:

    //performerId is parameter passed by method before
    Sessions.Where(x => x.Performers.Where(y => y.PerformerId == performerId).FirstOrDefault() != null)
    

    它现在在Java中是相同的(但我现在不在1.8项目上工作,我没有Java示例,但我期待它) .

  • 10

    如果你想了解lambda表达式的值,你不应该看一下只有之前存在的语言对应的新方法 . 这些例子怎么样:

    button.addActionListener( ev -> BACKGROUND_EXECUTOR_SERVICE.execute( () -> {
       String result = longComputation();
       SwingUtilities.invokeLater( () -> label.setText(result) );
    });
    

    只要考虑一下等效的Java 8之前的代码是什么样的,你会发现主要的优势不在于这里的性能 .

    如果你想看看Collection操作,这个怎么样?

    map.merge(key, 1, Integer::sum);
    

    如果 Map 中不存在该键,则会将 1 放入 Map 中,否则将 1 添加到现有 Map 的值中 . 再一次,想想旧对手的样子 . 事实上,如果 Map 具有适当的方法实现,它可能更有效,因为它避免了多个哈希操作,这只是一个额外的好处 .

    当然, forEach 方法无法在表达性方面提供如此大的收获,因为每种语言都是对应的 . 但是,如果声明需要重复长类型名称和通用参数,则不需要声明循环变量可以改进源代码 . 在 Map 的背景下尤其如此:

    Map<ContainerOrderFocusTraversalPolicy, List<AttributedCharacterIterator>> map;
    //…
    
    map.forEach((p,l)->l.forEach(i->{ /* do your work using p and i */}));
    

    在这里,这个新的迭代明显胜出

    for(Map.Entry<ContainerOrderFocusTraversalPolicy, List<AttributedCharacterIterator>> e:
                                                                          map.entrySet()) {
        ContainerOrderFocusTraversalPolicy p=e.getKey();
        for(AttributedCharacterIterator i: e.getValue()) {
            /* do your work using p and i */
        }
    }
    

    当然,只有实际的工作语句足够小才有意义,但这应该是如何使用lambda表达式:封装一小段代码 . 还有一些任务无法通过这种方式完成,并且需要一个普通的for-each循环,就像还有一些for-each循环无法完成的任务一样,需要更老的 for 循环处理一个 Iterator 手动......

  • 2

    在性能方面,与lambda相比,正常函数会更好,因为在groovy中存在闭包,它与lambda相同或更相似 .

    这些东西的工作方式就像你为任何集合编写一个闭包一样,它将在内部创建另一个类,它实际上为所提到的闭包或lambda表达式做了动作 .

    但是,通过使用lambda和闭包,我可以更好地迭代事物以及我可以轻松调试 . 您可以编写更少的代码行 .

  • 1

    在代码中使用lambda时,您会告诉我们该怎么做 . 上面的代码传递方法引用而不是循环遍历列表,我认为lambda更符合人体工程学 .

    就性能而言,打印100万件物品的时间几乎相同,但我还没有标记其他类型的操作 .

    上面的代码是一个简单的操作,但lambda有很多优点,因为你可以使用多个核心(并行流)很容易等传递的函数 .

  • 6
    import java.util.Arrays;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    import org.openjdk.jmh.annotations.Benchmark;
    import org.openjdk.jmh.annotations.BenchmarkMode;
    import org.openjdk.jmh.annotations.Fork;
    import org.openjdk.jmh.annotations.Measurement;
    import org.openjdk.jmh.annotations.Mode;
    import org.openjdk.jmh.annotations.Scope;
    import org.openjdk.jmh.annotations.State;
    import org.openjdk.jmh.annotations.Timeout;
    import org.openjdk.jmh.annotations.Warmup;
    
    /**
     *
     * @author devb
     */
    @BenchmarkMode(Mode.Throughput)
    @Fork(value = 1)
    @Warmup(iterations = 1, time = 32, timeUnit = TimeUnit.MILLISECONDS)
    @Measurement(iterations = 16, time = 1, timeUnit = TimeUnit.SECONDS)
    @Timeout(time = 1, timeUnit = TimeUnit.SECONDS)
    @State(Scope.Benchmark)
    public class CheckLamdaPerformance {
    
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    
        @Benchmark
        public void testPerOld() {
            //Old way:
            for (Integer n : list) {
                System.out.println(n);
            }
        }
    
        @Benchmark
        public void testPerNew() {
            //New way:
            list.forEach(n -> System.out.println(n));
        }
    
        @Benchmark
        public void testPerDoubleColon() {
            //or we can use :: double colon operator in Java 8
            list.forEach(System.out::println);
        }
    
    }
    

    测试基准

    import java.io.IOException;
    import org.openjdk.jmh.Main;
    import org.openjdk.jmh.runner.RunnerException;
    
    /**
     *
     * @author devb
     */
    public class MyBenchmark {
    
        private static final String TEST = ".*CheckLamdaPerformance.*"; 
    
        public static void main(String[] args) throws IOException, RunnerException {
            Main.main(getArguments(TEST, 1, 5000, 1));
        }
    
        private static String[] getArguments(String className, int nRuns, int runForMilliseconds, int nThreads) {
            return new String[]{className,
                "-i", "" + nRuns,
                "-r", runForMilliseconds + "ms",
                "-t", "" + nThreads,
                "-w", "5000ms",
                "-wi", "1"
            };
        }
    
    }
    

    运行输出后:

    # Run complete. Total time: 00:00:34
    
    Benchmark                                  Mode  Cnt     Score   Error  Units
    CheckLamdaPerformance.testPerDoubleColon  thrpt        776.008          ops/s
    CheckLamdaPerformance.testPerNew          thrpt       1096.423          ops/s
    CheckLamdaPerformance.testPerOld          thrpt        873.542          ops/s
    
  • 1

    请看Guava documentation中的第一部分 . 它是关于较旧的Java版本,但重要的是 - 在任何地方使用lambdas实际上可能会使代码的可读性降低 . 更好的示例可能是来自Stream API的示例:

    // Group employees by department
     Map<Department, List<Employee>> byDept
         = employees.stream()
                    .collect(Collectors.groupingBy(Employee::getDepartment));
    

相关问题