简体   繁体   English

Java中的Lambdas与Iterators

[英]Lambdas vs Iterators in Java

When writing Java code, NetBeans often encourages me to convert foreach loops (with iterators) to lambda expressions. 在编写Java代码时,NetBeans经常鼓励我将foreach循环(带有迭代器)转换为lambda表达式。 Sometimes the resulting code is much cleaner. 有时,生成的代码要干净得多。 Other times, the result is less clear than before. 其他时候,结果比以前更不清楚。

For example, the following uses iterators: 例如,以下使用迭代器:

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

And the equivalent uses lambda expressions: 等效项使用lambda表达式:

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

In this case, using lambda expressions results in lengthier code, and uses less intuitive language ( stream , filter , and forEach instead of for and if ). 在这种情况下,使用lambda表达式会导致代码更长,并且使用的直观语言更少( streamfilterforEach代替forif )。 So are there any advantages to using lambdas instead of iterators, when the code isn't cleaner? 那么,当代码不太干净时,使用lambda代替迭代器有什么好处吗? For example, are there any performance gains? 例如,是否有任何性能提升?

Iterate over collection and doing "something" with some of it's members is not the best example for using lambdas. 遍历集合并对其某些成员执行“操作”不是使用lambda的最佳示例。

Consider scenario like create result collection of objects filtered by some property, ordered by some other property and do some other "magic" and you'll see that lambdas saves dozens lines of code. 考虑这样的场景,例如创建按某种属性过滤,按某种其他属性排序的对象的结果集并进行其他“魔术”操作,您会看到lambda保存了数十行代码。

Hard to say if it's easier to read (probably not as lambda is yet another syntax you have to be familiar with) but after all - it's better to read one complex line of code instead creating anonymous/inner comparators. 很难说它是否更容易阅读(可能不是lambda是您必须熟悉的另一种语法),但毕竟-最好阅读一行复杂的代码,而不是创建匿名/内部比较器。 At least in C# lambda is very useful construction. 至少在C#中,lambda是非常有用的构造。

uses less intuitive language ( stream , filter , and forEach instead of for and if 使用不太直观的语言( streamfilterforEach代替forif

I don't really find them less intuitive. 我并没有发现它们不那么直观。 Moreover, wait for few months, those will be mostly used terms you will hear in Java. 此外,请等待几个月,这些都是您在Java中会听到的最常用的术语。 In fact, once you are comfortable with lambdas, you'll see how amazingly it cleansifies your code, that uses complex logic inside loops. 实际上,一旦您对lambda感到满意,您就会发现它清理代码的惊人程度,它在循环内部使用了复杂的逻辑。

So are there any advantages to using lambdas instead of iterators, when the code isn't cleaner? 那么,当代码不太干净时,使用lambda代替迭代器有什么好处吗? For example, are there any performance gains? 例如,是否有任何性能提升?

One obvious advantage of streams and lambdas that comes to my mind is, it gives you the power of parallel execution more easily using Stream.parallelStream() . 我想到的流和lambda的一个明显优势是,它使用Stream.parallelStream()更轻松地为您提供并行执行的能力。 Also internal iteration of streams gives control of how iteration happens to the API. 流的内部迭代还可以控制API如何进行迭代。 It can choose to evaluate intermediate operations lazily, parallely, sequentially, etc. Moreover, functional programming has it's own advantage. 它可以选择延迟,并行,顺序等方式评估中间操作。此外,函数式编程具有其自身的优势。 You can pass around logics in the form of lambdas, which used to be done using anonymous classes earlier. 您可以以lambda形式传递逻辑,该逻辑以前曾使用匿名类完成。 That way, some functionality can be easily re-used. 这样,可以轻松地重用某些功能。

Although there are certain disadvantages too, when comparing with normal for loop. 尽管也有某些缺点,但与普通的for循环相比。 If your loop is modifying some local variable, then you can't get it converted to forEach and lambdas version (at least not directly), because variables used inside lambdas needs to be effectively final . 如果您的循环正在修改某些局部变量,那么您就无法将其转换为forEach和lambdas版本(至少不是直接转换),因为lambdas内部使用的变量必须有效地是final

More details can be found on java.util.stream javadoc. 可以在java.util.stream javadoc上找到更多详细信息。

Just to give you some information about how I think you can write neat lambdas, is for your given code: 仅针对您给定的代码,向您提供一些有关我认为如何编写整齐的lambda的信息:

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

I would, convert it to the following: 我将其转换为以下内容:

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

I find the syntax like this much less intrusive and much clearer. 我发现这样的语法要少打扰和清晰得多。 Also, if you are going to need blocks inside your lambda, you are probably doing it wrong. 同样,如果您需要在lambda中使用块,则可能做错了。 Unless you have a very good reason to do so. 除非您有充分的理由这样做。

As an example if you would want to print every string on a new line, you could do: 例如,如果要在换行上打印每个字符串,可以执行以下操作:

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

Moreover, if you need to store the data in some kind of structure, you want to be using Collectors.* , and you do not want to be doing that inside your forEach , as a silly example we would like to convert it to a List<String> again: 此外,如果您需要以某种结构存储数据,则希望使用Collectors.* ,并且不想在forEach ,作为一个愚蠢的示例,我们希望将其转换为List<String>再次List<String>

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

Note that this particular implemention could have been done much easier if you were allowed to modify the original list. 请注意,如果允许您修改原始列表,则可以更轻松地完成此特定实现。

With respect to performance gains, I have written a simple benchmark test for lambda expressions versus iterators. 关于性能提升,我为lambda表达式和迭代器编写了一个简单的基准测试。 The test gives performance during warmup phase and non warmup phase. 该测试可提供预热阶段和非预热阶段的性能。 Warmup phase is considered for the first 10 iterations, non warmup phase later on. 在前10个迭代中考虑预热阶段,之后是非预热阶段。 The test runs for 10k iterations and measures the average time. 该测试运行10k次迭代,并测量平均时间。 Below is the code: 下面是代码:

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)));

    }
}

The output of the above code is as below: 上面代码的输出如下:

Avg Us during warmup using Iter: 1372.8821 使用Iter进行热身期间的平均值:1372.8821
Avg Us during warmup using Lambda: 5211.7064 使用Lambda进行热身期间的平均费用:5211.7064
Avg Us using Iter: 373.6436173173173 使用Iter估算平均值:373.6436173173173
Avg Us using Lambda: 370.77465015015014 使用Lambda估算平均值:370.77465015015014

Thus, as we can see the lambda expressions perform poorly in the warmup stages but post-warmup the performance with lambda expressions is quite similar to iterators. 因此,如我们所见,lambda表达式在预热阶段的性能很差,但是在预热后,lambda表达式的性能与迭代器非常相似。 This is just a simple test and if you are trying out a complex expression in your application, you can better profile the application and check which one works better in your case. 这只是一个简单的测试,如果您要在应用程序中尝试一个复杂的表达式,则可以更好地分析该应用程序,并检查哪种情况更适合您的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM