以下哪一项在Java 8中是更好的实践?

Java 8:

joins.forEach(join -> mIrc.join(mSession, join));

Java 7:

for (String join : joins) {
    mIrc.join(mSession, join);
}

我有很多for循环可以用lambdas来“简化”,但是使用它们真的有任何优势吗?它会提高性能和可读性吗?

EDIT

我还将把这个问题扩展到更长的方法。我知道你不能从lambda返回或打破父函数,这也应该在比较它们时考虑到,但还有其他要考虑的吗?


当前回答

Java 1.8 forEach方法相对于1.7 Enhanced for循环的优点是,在编写代码时,您可以只关注业务逻辑。

forEach方法将java.util.function.Consumer对象作为参数,因此它有助于将业务逻辑置于单独的位置,以便您可以随时重用它。

请看下面的片段,

Here I have created new Class that will override accept class method from Consumer Class, where you can add additional functionility, More than Iteration..!!!!!! class MyConsumer implements Consumer<Integer>{ @Override public void accept(Integer o) { System.out.println("Here you can also add your business logic that will work with Iteration and you can reuse it."+o); } } public class ForEachConsumer { public static void main(String[] args) { // Creating simple ArrayList. ArrayList<Integer> aList = new ArrayList<>(); for(int i=1;i<=10;i++) aList.add(i); //Calling forEach with customized Iterator. MyConsumer consumer = new MyConsumer(); aList.forEach(consumer); // Using Lambda Expression for Consumer. (Functional Interface) Consumer<Integer> lambda = (Integer o) ->{ System.out.println("Using Lambda Expression to iterate and do something else(BI).. "+o); }; aList.forEach(lambda); // Using Anonymous Inner Class. aList.forEach(new Consumer<Integer>(){ @Override public void accept(Integer o) { System.out.println("Calling with Anonymous Inner Class "+o); } }); } }

其他回答

TL;DR: List.stream(). foreach()是最快的。

我觉得我应该添加我的基准测试迭代的结果。 我采用了一个非常简单的方法(没有基准测试框架),并测试了5种不同的方法:

经典的 经典的foreach List.forEach () .forEach List.stream () () .forEach List.parallelStream ()

测试程序和参数

private List<Integer> list;
private final int size = 1_000_000;

public MyClass(){
    list = new ArrayList<>();
    Random rand = new Random();
    for (int i = 0; i < size; ++i) {
        list.add(rand.nextInt(size * 50));
    }    
}
private void doIt(Integer i) {
    i *= 2; //so it won't get JITed out
}

这个类中的列表将被迭代,并将一些doIt(Integer i)应用于它的所有成员,每次都通过不同的方法。 在Main类中,我运行了三次测试的方法来预热JVM。然后,我将测试方法运行1000次,并将每个迭代方法所花费的时间相加(使用System.nanoTime())。在这之后,我把这个和除以1000,这就是结果,平均时间。 例子:

myClass.fored();
myClass.fored();
myClass.fored();
for (int i = 0; i < reps; ++i) {
    begin = System.nanoTime();
    myClass.fored();
    end = System.nanoTime();
    nanoSum += end - begin;
}
System.out.println(nanoSum / reps);

我在i5 4核CPU上运行这个程序,java版本为1.8.0_05

经典的

for(int i = 0, l = list.size(); i < l; ++i) {
    doIt(list.get(i));
}

执行时间:4.21 ms

经典的foreach

for(Integer i : list) {
    doIt(i);
}

执行时间:5.95毫秒

List.forEach ()

list.forEach((i) -> doIt(i));

执行时间:3.11 ms

.forEach List.stream () ()

list.stream().forEach((i) -> doIt(i));

执行时间:2.79 ms

.forEach List.parallelStream ()

list.parallelStream().forEach((i) -> doIt(i));

执行时间:3.6 ms

更好的做法是使用for-each。除了违反Keep It Simple, Stupid原则外,新发明的forEach()至少有以下不足:

不能使用非最终变量。因此,像下面这样的代码不能转换为forEach lambda:

对象prev = null; for(对象curr: list) { If (prev != null) foo(上一页,咕咕叫); Prev = curr; }

Can't handle checked exceptions. Lambdas aren't actually forbidden from throwing checked exceptions, but common functional interfaces like Consumer don't declare any. Therefore, any code that throws checked exceptions must wrap them in try-catch or Throwables.propagate(). But even if you do that, it's not always clear what happens to the thrown exception. It could get swallowed somewhere in the guts of forEach() Limited flow-control. A return in a lambda equals a continue in a for-each, but there is no equivalent to a break. It's also difficult to do things like return values, short circuit, or set flags (which would have alleviated things a bit, if it wasn't a violation of the no non-final variables rule). "This is not just an optimization, but critical when you consider that some sequences (like reading the lines in a file) may have side-effects, or you may have an infinite sequence." Might execute in parallel, which is a horrible, horrible thing for all but the 0.1% of your code that needs to be optimized. Any parallel code has to be thought through (even if it doesn't use locks, volatiles, and other particularly nasty aspects of traditional multi-threaded execution). Any bug will be tough to find. Might hurt performance, because the JIT can't optimize forEach()+lambda to the same extent as plain loops, especially now that lambdas are new. By "optimization" I do not mean the overhead of calling lambdas (which is small), but to the sophisticated analysis and transformation that the modern JIT compiler performs on running code. If you do need parallelism, it is probably much faster and not much more difficult to use an ExecutorService. Streams are both automagical (read: don't know much about your problem) and use a specialized (read: inefficient for the general case) parallelization strategy (fork-join recursive decomposition). Makes debugging more confusing, because of the nested call hierarchy and, god forbid, parallel execution. The debugger may have issues displaying variables from the surrounding code, and things like step-through may not work as expected. Streams in general are more difficult to code, read, and debug. Actually, this is true of complex "fluent" APIs in general. The combination of complex single statements, heavy use of generics, and lack of intermediate variables conspire to produce confusing error messages and frustrate debugging. Instead of "this method doesn't have an overload for type X" you get an error message closer to "somewhere you messed up the types, but we don't know where or how." Similarly, you can't step through and examine things in a debugger as easily as when the code is broken into multiple statements, and intermediate values are saved to variables. Finally, reading the code and understanding the types and behavior at each stage of execution may be non-trivial. Sticks out like a sore thumb. The Java language already has the for-each statement. Why replace it with a function call? Why encourage hiding side-effects somewhere in expressions? Why encourage unwieldy one-liners? Mixing regular for-each and new forEach willy-nilly is bad style. Code should speak in idioms (patterns that are quick to comprehend due to their repetition), and the fewer idioms are used the clearer the code is and less time is spent deciding which idiom to use (a big time-drain for perfectionists like myself!).

如您所见,我不是forEach()的忠实粉丝,除非在它有意义的情况下。

对我来说特别讨厌的是Stream没有实现Iterable(尽管实际上有方法迭代器),并且不能在for-each中使用,只能在forEach()中使用。我建议使用(Iterable<T>)stream::iterator将Streams转换为Iterables。更好的选择是使用StreamEx,它修复了许多流API问题,包括实现Iterable。

也就是说,forEach()在以下方面是有用的:

Atomically iterating over a synchronized list. Prior to this, a list generated with Collections.synchronizedList() was atomic with respect to things like get or set, but was not thread-safe when iterating. Parallel execution (using an appropriate parallel stream). This saves you a few lines of code vs using an ExecutorService, if your problem matches the performance assumptions built into Streams and Spliterators. Specific containers which, like the synchronized list, benefit from being in control of iteration (although this is largely theoretical unless people can bring up more examples) Calling a single function more cleanly by using forEach() and a method reference argument (ie, list.forEach (obj::someMethod)). However, keep in mind the points on checked exceptions, more difficult debugging, and reducing the number of idioms you use when writing code.

我参考的文章:

关于Java 8的一切 由内而外的迭代(正如另一张海报所指出的)

编辑:看起来lambdas的一些原始建议(例如http://www.javac.info/closures-v06a.html谷歌Cache)解决了我提到的一些问题(当然,同时增加了它们自己的复杂性)。

forEach()可以实现得比for-each循环快,因为迭代对象知道迭代其元素的最佳方式,而不是标准迭代器方式。所以区别在于内部循环还是外部循环。

例如,ArrayList.forEach(action)可以简单地实现为

for(int i=0; i<size; i++)
    action.accept(elements[i])

与for-each循环相反,for-each循环需要大量的脚手架

Iterator iter = list.iterator();
while(iter.hasNext())
    Object next = iter.next();
    do something with `next`

但是,通过使用forEach(),我们还需要考虑两个开销成本,一个是生成lambda对象,另一个是调用lambda方法。它们可能并不重要。

请参见http://journal.stuffwithstuff.com/2013/01/13/iteration-inside-and-out/,以比较不同用例的内部/外部迭代。

当操作可以并行执行时,优势就显现出来了。(参见http://java.dzone.com/articles/devoxx-2012-java-8-lambda-and -关于内部和外部迭代的部分)

从我的观点来看,主要的优点是可以定义在循环中要做的事情的实现,而不必决定它是并行执行还是顺序执行 如果你想让你的循环并行执行,你可以简单地写 joins.parallelStream()。forEach(join -> mIrc。加入(mSession加入)); 你将不得不为线程处理等编写一些额外的代码。

注意:对于我的回答,我假设连接实现java.util.Stream接口。如果联接只实现java.util.Iterable接口,则不再是这样。

forEach函数最令人讨厌的限制之一是缺乏受控异常支持。

一个可能的解决方法是用普通的forEach循环替换终端forEach:

    Stream<String> stream = Stream.of("", "1", "2", "3").filter(s -> !s.isEmpty());
    Iterable<String> iterable = stream::iterator;
    for (String s : iterable) {
        fileWriter.append(s);
    }

以下是lambdas和流中检查异常处理的其他变通方法中最常见的问题:

Java 8 Lambda函数抛出异常?

Java 8: Lambda-Streams,过滤方法与异常

我如何从Java 8流内部抛出CHECKED异常?

Java 8:在lambda表达式中强制检查异常处理。为什么是强制性的,而不是选择性的?