Java 8stream:多个filter与复杂的条件

有时候你想过滤一个Stream并且有多个条件:

 myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ... 

或者你可以做一个复杂的条件和一个单一的 filter

 myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ... 

我的猜测是,第二种方法有更好的性能特点,但我不知道

第一种方法赢得了可读性,但是什么更好的performance?

这两个选项必须执行的代码非常相似,以至于无法可靠地预测结果。 底层的对象结构可能会有所不同,但这对热点优化器来说并不是什么挑战。 所以这取决于其他的周围条件,如果有任何区别的话,这将会产生更快的执行速度。

结合两个filter实例创build更多的对象,因此更多的委托代码,但如果您使用方法引用而不是lambdaexpression式,例如通过filter(ItemType::isCool)replacefilter(x -> x.isCool()) ,可以filter(ItemType::isCool) 。 这样你就消除了为你的lambdaexpression式创build的综合委托方法。 因此,使用两个方法引用组合两个filter可能会创build相同或较less的委托代码,而不是使用带有&&的lambdaexpression式的单个filter调用。

但是,正如所说,这种开销将被HotSpot优化器消除,可以忽略不计。

从理论上讲,两个滤波器可以比单个滤波器更容易并行化,但这只与计算量较大的任务有关。

所以没有简单的答案。

底线是,不要考虑在气味检测阈值以下的性能差异。 使用什么更可读。

在一个Set中,我有一万个对不同对象的引用。 然后我做了“速度testing”:

 long time1 = System.currentTimeMillis(); users.stream() .filter((u) -> u.getGender() == Gender.FEMALE && u.getAge() % 2 == 0); long time2 = System.currentTimeMillis(); System.out.printf("%d milli seconds", time2 - time1); 

执行10次后,平均时间为45.5毫秒。 接下来,我使用多个filter():

 long time1 = System.currentTimeMillis(); users.stream() .filter((u) -> u.getGender() == Gender.FEMALE) .filter((u) -> u.getAge() % 2 == 0); long time2 = System.currentTimeMillis(); System.out.printf("%d milli seconds", time2 - time1); 

平均时间是44,7毫秒。

当然这是一个简单的操作testing,但我相信没有什么区别,因为Stream使用内部迭代,而不像使用外部迭代的“for”循环,换句话说,循环只发生一次,并且在完成之后回。 编译器读取filter的时间可能不相同,这是微不足道的。

这个testing表明你的第二个选项可以显着改善。 首先发现,然后是代码:

 one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=4142, min=29, average=41.420000, max=82} two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=13315, min=117, average=133.150000, max=153} one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10320, min=82, average=103.200000, max=127} 

现在的代码:

 enum Gender { FEMALE, MALE } static class User { Gender gender; int age; public User(Gender gender, int age){ this.gender = gender; this.age = age; } public Gender getGender() { return gender; } public void setGender(Gender gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } static long test1(List<User> users){ long time1 = System.currentTimeMillis(); users.stream() .filter((u) -> u.getGender() == Gender.FEMALE && u.getAge() % 2 == 0) .allMatch(u -> true); // least overhead terminal function I can think of long time2 = System.currentTimeMillis(); return time2 - time1; } static long test2(List<User> users){ long time1 = System.currentTimeMillis(); users.stream() .filter(u -> u.getGender() == Gender.FEMALE) .filter(u -> u.getAge() % 2 == 0) .allMatch(u -> true); // least overhead terminal function I can think of long time2 = System.currentTimeMillis(); return time2 - time1; } static long test3(List<User> users){ long time1 = System.currentTimeMillis(); users.stream() .filter(((Predicate<User>) u -> u.getGender() == Gender.FEMALE).and(u -> u.getAge() % 2 == 0)) .allMatch(u -> true); // least overhead terminal function I can think of long time2 = System.currentTimeMillis(); return time2 - time1; } public static void main(String... args) { int size = 10000000; List<User> users = IntStream.range(0,size) .mapToObj(i -> i % 2 == 0 ? new User(Gender.MALE, i % 100) : new User(Gender.FEMALE, i % 100)) .collect(Collectors.toCollection(()->new ArrayList<>(size))); repeat("one filter with predicate of form u -> exp1 && exp2", users, Temp::test1, 100); repeat("two filters with predicates of form u -> exp1", users, Temp::test2, 100); repeat("one filter with predicate of form predOne.and(pred2)", users, Temp::test3, 100); } private static void repeat(String name, List<User> users, ToLongFunction<List<User>> test, int iterations) { System.out.println(name + ", list size " + users.size() + ", averaged over " + iterations + " runs: " + IntStream.range(0, iterations) .mapToLong(i -> test.applyAsLong(users)) .summaryStatistics()); }