Lambdas与Java中的迭代器

时间:2014-03-25 19:42:26

标签: java performance lambda iterator java-8

编写Java代码时,NetBeans经常鼓励我将foreach循环(使用迭代器)转换为lambda表达式。有时候生成的代码更清晰。其他时候,结果不如以前那么清晰。

例如,以下使用迭代器:

List<String> list = new ArrayList<>();
for (String str : list) {
    if (str.charAt(0) == ' ')) {
        // do something with str
    }
}

等效使用lambda表达式:

List<String> list = new ArrayList<>();
list.stream().filter((str) -> (str.charAt(0) == ' ')).forEach((str) -> {
    // do something with str
)};

在这种情况下,使用lambda表达式会导致代码更长,并使用不太直观的语言(streamfilterforEach而不是for和{{1 }})。因此,当代码不清晰时,使用lambdas而不是迭代器有什么好处吗?例如,是否有任何性能提升?

4 个答案:

答案 0 :(得分:3)

迭代收集并对其中某些成员执行“某些操作”并不是使用lambdas的最佳示例。

考虑如下场景,比如创建由某些属性过滤的对象的结果集合,按其他属性排序并执行其他一些“魔术”,您将看到lambdas可以节省数十行代码。

很难说它是否更容易阅读(可能不像lambda是你必须熟悉的另一种语法)但毕竟 - 最好是阅读一行复杂的代码而不是创建匿名/内部比较器。至少在C#中lambda非常有用。

答案 1 :(得分:2)

  

使用不太直观的语言(streamfilterforEach代替forif

我真的觉得它们不那么直观。而且,等待几个月,这些将主要用于您将在Java中听到的术语。事实上,一旦你对lambdas感到满意,你会发现它清理你的代码是多么令人惊奇,它在循环中使用了复杂的逻辑。

  

当代码不干净时,使用lambdas而不是迭代器有什么好处吗?例如,是否有任何性能提升?

我想到的流和lambdas的一个明显优势是,它使用Stream.parallelStream()更轻松地为您提供并行执行的强大功能。此外,流的内部迭代可控制API发生迭代的方式。它可以选择懒惰,平行,顺序等评估中间操作。此外,函数式编程有其自身的优势。您可以以lambdas的形式传递逻辑,以前使用匿名类完成。这样,一些功能可以很容易地重复使用。

虽然与普通for循环相比也存在某些缺点。如果您的循环正在修改某个局部变量,那么您无法将其转换为forEach和lambdas版本(至少不是直接),因为lambdas中使用的变量需要有效final

更多细节可以在java.util.stream javadoc找到。

答案 2 :(得分:2)

只是为了给你一些关于我认为你能写出整齐的lambdas的信息,这是给你的代码:

List<String> listString = new ArrayList<>();
for (String str : listString) {
    if (str.charAt(0) == ' ') {
        // do something with str
    }
}

我愿意,将其转换为以下内容:

List<String> listString = new ArrayList<>();
listString.stream()
        .filter(s -> s.charAt(0) == ' ')
        .forEach(/*do something*/);

我觉得这样的语法不那么具有干扰性和清晰度。另外,如果你在lambda中需要块,你可能做错了。除非你有充分的理由这样做。

例如,如果您想要在新行上打印每个字符串,您可以这样做:

List<String> listString = new ArrayList<>();
listString.stream()
        .filter(s -> s.charAt(0) == ' ')
        .forEach(System.out::println);

此外,如果您需要以某种结构存储数据,您希望使用Collectors.*,并且您不希望在forEach内执行此操作,这是一个愚蠢的例子我们想将其再次转换为List<String>

List<String> listString = new ArrayList<>();
List<String> filteredString = listString.stream()
        .filter(s -> s.charAt(0) == ' ')
        .collect(Collectors.toList());

请注意,如果允许您修改原始列表,则可以更轻松地完成此特定实现。

答案 3 :(得分:1)

关于性能提升,我已经为lambda表达式与迭代器编写了一个简单的基准测试。该测试在预热阶段和非预热阶段提供性能。前10次迭代考虑预热阶段,后续非预热阶段。测试运行10k次迭代并测量平均时间。以下是代码:

import java.util.ArrayList;
import java.util.List;

public class LambdaTest {

private static final int COUNT = 10000;
public static void main(String[] args) {

    List<String> str = new ArrayList<String>();
    for (int i =0; i<100000; i++){
        str.add(""+i);
    }
    double iterTotal = 0;
    double lambdaTotal = 0;
    double warmupIterTotal = 0;
    double warmupLambdaTotal = 0;
    for(int j = 0; j < COUNT; j++){
        long start = System.nanoTime();
        for (int i = 0; i< 100000; i++){
            String string = str.get(i);
//              if(string.length() < 5){
//                  System.out.println(string);
//              }
        }
        long end = System.nanoTime();
        if(j>=10){
            iterTotal += end - start;
        }else {
            warmupIterTotal += end - start;
        }
        System.out.println("Output 1 in : "+(end-start)*1.0/1000 +" Us");
        start = System.nanoTime();
        str.forEach((string) -> {
//              if(string.length() < 5){
//                  System.out.println(string);
//              }
        }); 
        end = System.nanoTime();
        if(j>=10){
            lambdaTotal+= end-start;
        }else {
            warmupLambdaTotal += end - start;
        }
        System.out.println("Output 2 in : "+(end-start)*1.0/1000 +" Us");
    }

    System.out.println("Avg Us during warmup using Iter: "+warmupIterTotal/(1000*10));
    System.out.println("Avg Us during warmup using Lambda: "+warmupLambdaTotal/(1000*10));

    System.out.println("Avg Us using Iter: "+iterTotal/(1000*(COUNT-10)));
    System.out.println("Avg Us using Lambda: "+lambdaTotal/(1000*(COUNT-10)));

    }
}

以上代码的输出如下:

使用Iter:1372.8821的热身期间平均我们 在使用Lambda进行热身时平均我们:5211.7064
使用Iter平均值:373.6436173173173
使用Lambda平均值:370.77465015015014

因此,正如我们所看到的,lambda表达式在预热阶段表现不佳,但是后期预热,使用lambda表达式的性能与迭代器非常相似。这只是一个简单的测试,如果您在应用程序中尝试复杂的表达式,则可以更好地分析应用程序,并检查哪个应用程序在您的情况下效果更好。