以下哪一项在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返回或打破父函数,这也应该在比较它们时考虑到,但还有其他要考虑的吗?
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/,以比较不同用例的内部/外部迭代。
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表达式中强制检查异常处理。为什么是强制性的,而不是选择性的?
我觉得我有必要扩展一下我的评论……
关于范式\风格
这可能是最值得注意的方面。FP之所以流行是因为你可以避免副作用。我不会深入研究你能从中得到什么优点\缺点,因为这与问题无关。
不过,我要说的是使用Iterable的迭代。forEach的灵感来自FP,是将更多的FP引入Java的结果(具有讽刺意味的是,我要说forEach在纯FP中没有多大用处,因为它除了引入副作用之外什么都不做)。
最后,我想说的是,这是你目前写作的品味、风格和范式的问题。
并行性。
从性能的角度来看,使用Iterable并没有明显的好处。一个接一个(…)。
根据Iterable的官方文档。forEach:
对象中Iterable的内容执行给定的操作
顺序元素在迭代时发生,直到所有元素都已完成
已处理或操作抛出异常。
... 例如,文档非常清楚,将没有隐式并行。增加一个将违反LSP。
现在,Java 8中承诺了“并行集合”,但要使用这些集合,你需要更加明确,并在使用它们时多加注意(例如,请参阅mschenk74的回答)。
顺便说一句:在这种情况下,流。forEach将被使用,并且它不保证实际工作将并行完成(取决于底层集合)。
更新:可能不是那么明显和有点拉伸一眼,但有另一个方面的风格和可读性的观点。
首先,简单的forloop是简单而古老的。大家都已经知道了。
第二,也是更重要的——你可能想要使用Iterable。forEach只使用一行程序lambdas。如果“身体”变重了,它们就不那么可读了。
从这里你有两个选择-使用内部类(讨厌)或使用普通的旧forloop。
当人们看到相同的事情(迭代集合)在相同的代码库中进行不同的vays/风格时,他们经常会感到恼火,这似乎是事实。
同样,这可能是问题,也可能不是问题。这取决于编写代码的人。
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);
}
});
}
}
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/,以比较不同用例的内部/外部迭代。