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

}