Java stream.max()是否总是比stream.reduce()快?
我有以下代码片段Java stream.max()是否总是比stream.reduce()快?,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,我有以下代码片段 // List of persons with name and age List<Person> persons = new ArrayList<>(); // Adding 10,000 objects for(int i = 0 ; i < 10000 ; i ++) { Person p = new Person(); p.setName("Person " +
// List of persons with name and age
List<Person> persons = new ArrayList<>();
// Adding 10,000 objects
for(int i = 0 ; i < 10000 ; i ++) {
Person p = new Person();
p.setName("Person " + i);
p.setAge(i);
persons.add(p);
}
long time1 = System.nanoTime();
System.out.println("Time before steram.reduce()" + time1);
Optional<Person> o1 = Optional<Person> o1 = persons.stream().reduce(BinaryOperator.maxBy(Comparator.comparingInt(p -> p.getAge())));
long time2 = System.nanoTime();
System.out.println(o1.get() + "\nTime after stream.reduce() " + time2);
System.out.println("**** Rough execution time for stream.reduce() : " + (time2 - time1) + " nano secs");
long time3 = System.nanoTime();
System.out.println("Time before stream.max() " + time3);
Optional<Person> o2 = persons.stream().max((p01, p02) -> p01.getAge() - p02.getAge());
long time4 = System.nanoTime();
System.out.println(o2.get() + "\nTime after stream.max() " + time4);
System.out.println("**** Rough execution time for stream.max() : " + (time4 - time3) + " nano secs");
p.S.我多次运行此代码,更改了stream.max()
和stream.reduce()
的顺序,并发现stream.reduce()
比stream.max()
花费的时间要多得多
那么
stream.max()
总是比stream.reduce()快吗?如果是,那么我们什么时候应该使用stream.reduce()
?您的reduce函数在每次迭代中计算getAge
两次,这就是为什么根据编译器的优化,结果可能会变慢,重新构造代码并检查结果
另外,Stream.max
可能受益于内置VM优化,因此您应该始终坚持使用内置函数,而不是实现等效函数。您的reduce函数在每次迭代中计算getAge
两次,这就是为什么根据编译器优化结果可能会变慢的原因,重新构造代码并检查结果
另外,Stream.max
可能受益于内置的VM优化,因此您应该始终坚持使用内置函数,而不是实现等效函数。如下所示:
public final Optional<P_OUT> max(Comparator<? super P_OUT> comparator) {
return reduce(BinaryOperator.maxBy(comparator));
}
公共最终可选最大值(比较器如下所示:
public final Optional<P_OUT> max(Comparator<? super P_OUT> comparator) {
return reduce(BinaryOperator.maxBy(comparator));
}
public-final-Optional-max(比较准确的基准测试确实很难构建。例如,尝试更改比较代码段的顺序(即先max,然后reduce),您可能会看到一些差异。无论如何,max在IMHO中更具可读性,所以我会选择max。如果您稍后注意到,reduce确实会提高您的整个软件性能,那么就选择它。但是,首先分析,然后优化;)new Date().getTime()
是获取System.currentTimeMillis()的一种麻烦的方法
。但是您应该使用System.nanoTime()
来测量经过的时间,或者最好使用Comparator.comparingit(Person::getAge())
而不是(p01,p02)->p01.getAge()-p02.getAge()读取所有内容顺便说一句,
的效率会更高。max
可能比reduce
的效率更高,但单次运行两个元素流还不足以得出结论。通常,两者的时间复杂度相同,因此您可能体验到的差异,即使是可重复的,也会有所不同当你处理数千个元素的流时变得无关紧要…@Klitos Kyriacou:你不会重复自己,也不容易出现溢出错误。此外,你正在重用可能被其他人使用的现有代码,因此,可能比新引入的自定义代码具有更好的运行时优化状态。准确的基准测试确实非常重要难以构建。例如,尝试更改比较代码段的顺序(即先最大后减少),您可能会看到一些差异。无论如何,max在IMHO中更具可读性,所以我会选择max。如果您稍后注意到,reduce确实会提高您的整个软件性能,那么就选择它。但是,首先分析,然后优化;)new Date().getTime()
是获取System.currentTimeMillis()的一种麻烦的方法
。但是您应该使用System.nanoTime()
来测量经过的时间,或者最好使用Comparator来读取所有内容。comparingit(Person::getAge())
而不是(p01,p02)->p01.getAge()-p02.getAge()
将更加有效,顺便说一句,我只是想说清楚:max
可能比reduce
更有效,但一次运行两个元素流不足以得出结论。一般来说,两者都具有相同的时间复杂性,因此当你处理数千个元素流时,你可能体验到的差异,即使是可复制的,也将变得无关紧要。@Klitos Kyriacou:你不会重复自己,也不会出现溢出错误。此外,您正在重用可能被其他人使用的现有代码,因此,可能比新引入的自定义代码具有更好的运行时优化状态。Stream.max
变量也会对每个元素调用两次getAge
。我认为它会实现得更智能,但正如我在OpenJDK中看到的,它只调用了“reduce”(BinaryOperator.maxBy(comparator))。所以我现在想到的唯一一件事是,比较器可能比这种内联if
分支优化得更好。如果你所拥有的只是比较器,那么没有办法更聪明地实现它。如果该方法知道它实际上是一个“max by属性”,它可以做得更智能些,但是Comparator
接口并没有告诉我们,Stream.max
变量也会对每个元素调用getAge
两次。我认为它会实现得更智能些,但正如我在OpenJDK中看到的,它只调用了'reduce(BinaryOperator.maxBy(Comparator))因此,我现在想到的唯一一件事是,比较器可能比这种内联的如果分支优化得更好。如果你只有一个比较器,那么就没有办法更聪明地实现它了。如果这个方法知道它实际上是一个“max by property”,它可以做得更聪明,但是比较器接口不能告诉我…我有不同的基准测试结果,第一个测试结果实际上更快,其背后的原因可能是它直接比较整数,而第二个先减去并比较为0(或者可能是由于使用比较器造成的额外间接影响)。这并没有优化掉,正如JIT发出的代码中所示。@jornverne它不能优化掉,因为它会产生不同的行为。但是,我没有在这里分析JIT代码-只是想指出基本上没有(可测量的)差异-在您的案例中,差异是否显著?差异是最明显的迹象
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class MaxReducePerformance
{
public static void main(String[] args)
{
for (int n=500000; n<=5000000; n+=500000)
{
List<Person> persons = new ArrayList<>();
for (int i = 0; i < n; i++)
{
Person p = new Person();
p.setName("Person " + i);
p.setAge(i);
persons.add(p);
}
System.out.println("For " + n);
long time1 = System.nanoTime();
Optional<Person> o1 = persons.stream().reduce((p01, p02) ->
{
if (p01.getAge() < p02.getAge())
return p02;
return p01;
});
long time2 = System.nanoTime();
double d0 = (time2 - time1) / 1e9;
System.out.println("Reduce: "+d0+" seconds, " + o1);
long time3 = System.nanoTime();
Optional<Person> o2 =persons.stream().max(
(p01, p02) -> p01.getAge() - p02.getAge());
long time4 = System.nanoTime();
double d1 = (time4 - time3) / 1e9;
System.out.println("Max : "+d1+" seconds, " + o2);
}
}
}
class Person
{
String name;
int age;
void setName(String name)
{
this.name = name;
}
void setAge(int age)
{
this.age = age;
}
int getAge()
{
return age;
}
}