背景:
在做發(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 83957478 85 average=82ms - 2 filters
79 82 79 82 78 78 86887784 average=81ms
對(duì)照組2:
- 1 filter with sorted
104129103 102 95 97 98 9290101 average = 99ms - 2 filters with sorted
112 114136113 114 121 112 126 125111average = 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è)試下)