Java Stream API中多個(gè)和單個(gè)filter有性能差異嗎?

背景:

在做發(fā)布前code review時(shí),看到下面這段代碼

workShiftOrderDTOS = workShiftOrderDTOS.stream()
        .filter(x -> x.getShiftDO().isFullDayWorkShift() == false)
        .collect(Collectors.toList());

開(kāi)發(fā)同學(xué)新增了1個(gè)篩選條件

workShiftOrderDTOS = workShiftOrderDTOS.stream()
        .filter(x -> x.getShiftDO().isFullDayWorkShift() == false)
        .filter(x -> x.getShiftDO().canViewShift())
        .collect(Collectors.toList());

思考

由上面的code review,思考下面2段對(duì)集合進(jìn)行filter的代碼,返回結(jié)果是一致的,性能上有差異嘛?

    @Data
    class Employee {
        private String gender;
        private Integer age;
    }

    public static void main(String[] args) {
        List<Employee> employees = Lists.newArrayList();

        // >1個(gè)filter
        employees.stream()
            .filter(employee -> employee.getAge() > 32)
            .filter(employee -> "male".equals(employee.getGender()));
        
        // 1個(gè)filter
        employees.stream()
            .filter(employee ->
                employee.getAge() > 32
                    && "male".equals(employee.getGender()));
    }

我們先看下編譯后的class,可以看到編譯器并未做任何優(yōu)化:


于是寫(xiě)了4段測(cè)試代碼作為對(duì)照組,執(zhí)行對(duì)比耗時(shí):
1 filter:

public static void main(String[] args) {
    List<Employee> employees = Lists.newArrayList();

    Random rand = new Random();

    for (int i = 1; i <= 100000; i++) {
        Employee employee = new Employee();

        employee.setAge(i % 100);
        employee.setGender(i % 2 == 0 ? "male" : "female");

        employees.add(employee);
    }

    StopWatch watch = new StopWatch();
    
    watch.start();
    employees.stream()
        .filter(employee ->
            employee.getAge() > 32
                && "male".equals(employee.getGender()))
        .collect(Collectors.toList());
    watch.stop();
    System.out.println(watch.getTotalTimeMillis());
}

2 filters:

public static void main(String[] args) {
    List<Employee> employees = Lists.newArrayList();

    Random rand = new Random();

    for (int i = 1; i <= 100000; i++) {
        Employee employee = new Employee();

        employee.setAge(i % 100);
        employee.setGender(i % 2 == 0 ? "male" : "female");

        employees.add(employee);
    }

    StopWatch watch = new StopWatch();

    watch.start();
    employees.stream()
        .filter(employee -> employee.getAge() > 32)
        .filter(employee -> "male".equals(employee.getGender()))
        .collect(Collectors.toList());
    watch.stop();
    System.out.println(watch.getTotalTimeMillis());
}

1 filter with sorted

public static void main(String[] args) {
    List<Employee> employees = Lists.newArrayList();

    Random rand = new Random();

    for (int i = 1; i <= 100000; i++) {
        Employee employee = new Employee();

        employee.setAge(i % 100);
        employee.setGender(i % 2 == 0 ? "male" : "female");

        employees.add(employee);
    }

    StopWatch watch = new StopWatch();

    watch.start();
    employees.stream()
        .filter(employee ->
            employee.getAge() > 32
                && "male".equals(employee.getGender()))
        .sorted(Comparator.comparingInt(Employee::getAge))
        .collect(Collectors.toList());
    watch.stop();
    System.out.println(watch.getTotalTimeMillis());
}

2 filters with sorted

public static void main(String[] args) {
    List<Employee> employees = Lists.newArrayList();

    Random rand = new Random();

    for (int i = 1; i <= 100000; i++) {
        Employee employee = new Employee();

        employee.setAge(i % 100);
        employee.setGender(i % 2 == 0 ? "male" : "female");

        employees.add(employee);
    }

    StopWatch watch = new StopWatch();

    watch.start();
    employees.stream()
        .filter(employee -> employee.getAge() > 32)
        .sorted(Comparator.comparingInt(Employee::getAge))
        .filter(employee -> "male".equals(employee.getGender()))
        .collect(Collectors.toList());
    watch.stop();
    System.out.println(watch.getTotalTimeMillis());
}

各運(yùn)行10次,分別去掉最高、最低耗時(shí)后求平均值:

對(duì)照組1:

  • 1 filter
    86 77 80 87 80 83 95 74 78 85 average=82ms
  • 2 filters
    79 82 79 82 78 78 86 88 77 84 average=81ms

對(duì)照組2:

  • 1 filter with sorted
    104 129 103 102 95 97 98 92 90 101 average = 99ms
  • 2 filters with sorted
    112 114 136 113 114 121 112 126 125 111 average = 117ms

發(fā)現(xiàn)了什么規(guī)律?

  • 1個(gè)filter和2個(gè)連續(xù)的filter性能上并無(wú)差異。
  • 當(dāng)2個(gè)filter間增加了sorted操作后,這時(shí)候把filter合并性能更優(yōu)。

這和Stream API的原理推導(dǎo)出來(lái)的結(jié)論是一致的,有興趣的可以百度看下Stream API原理(了解stateful op和stateless op、Stage、Sink接口)。

再延伸思考下:
1 filter和3 filters->n filters有性能差異么?(理論應(yīng)該是沒(méi)有的,可以實(shí)際代碼測(cè)試下)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容