【问题标题】:What's better in terms of performance of a Java stream: combine filters or combine circumstances? [duplicate]在 Java 流的性能方面哪个更好:组合过滤器或组合环境? [复制]
【发布时间】:2018-10-11 14:02:55
【问题描述】:

需要过滤适合其字段的某些情况的所有对象。 假设对象有几个字段:

class Price {
int first;
int second; 
String third; 
}

和价格流:

Stream<Price> streamPrices; 

什么是最好的选择:

1.

streamPrices
    .parallel()
    .filter(p0->p0.first> 10)
    .filter(p1->p1.second <30)
    .filter(p2-> p2.third.length() > 8);

2.

streamPrices
   .parallel()
   .filter(p-> (p.first > 10) 
      && (p->p.second <30) 
      && (p-> p.third.length() > 8)
    );

我使用了 JMH 框架,但没有得出明确的结论。

【问题讨论】:

  • 我认为第一个更具可读性,这是您在实际遇到性能问题之前唯一应该考虑的事情。
  • 第二个示例是有效的 Java 语法吗?我认为您不能将谓词与逻辑和运算符结合起来。我认为您想删除 lamdas 并仅使用布尔表达式。

标签: java parallel-processing java-stream


【解决方案1】:

不用担心性能,毕竟过早的优化是万恶之源。它通常远没有精心设计的可读且易于维护的代码那么重要。

衡量性能很棘手,尤其是在涉及streams 时。例如,对于较小的列表,您可能会发现 parallelsequential 慢,因为线程的开销超过了收益。您还会发现 streams 通常比传统的 iterators 效率低,因为流是编译器将其转换为对象和方法调用的语法糖。

话虽如此,差异是如此微不足道,以至于根本不值得考虑。

【讨论】:

    【解决方案2】:

    使用您的价格等级进行测量,其中第一个过滤器去除 50%,第二个过滤器去除下一个 50%,第三个过滤器去除 50%,结果表明方法 2 所需的时间是方法 1 的一半多一点。

    编辑:按照 Lino 在 cmets 中的建议改进了数字,略微减少了数字,但方法 1 总体减少了 50%。

    方法 1:118 毫秒 / 10000000 项

    平均:118 毫秒,最小:107 毫秒,最大:252 毫秒

    方法 2:79 毫秒 / 10000000 项

    平均:79 毫秒,最小:70 毫秒,最大:91 毫秒

    因此,方法 2 的性能肯定更高,但在我的计算机上实际差异小于 40 毫秒/1000 万个项目,因此除非您使用非常大的集合,否则它可以忽略不计。

    我个人更喜欢方法 1,因为它更易于阅读。如果您从另一个函数传递谓词,方法 2 会很有用。

    public class FilterTest extends Test {
    int i = 10000000;
    Random random = new Random(i);
    List<Price> prices;
    
    public void setup() {
        prices = Stream.generate(new Supplier<Price>() {
            @Override
            public Price get() {
                return new Price(random);
            }
        }).limit(i).collect(Collectors.toList());
    }
    
    @Override
    public void run() {
        // Method 1
        // prices.stream().parallel()
        //     .filter(p -> p.v1 > 0.5d)
        //     .filter(p -> p.v2 > 0.5d)
        //     .filter(p -> p.s.length() > 16)
        //     .collect(Collectors.toList());
    
        // Method 2
        prices.stream().parallel()
                .filter(p -> p.v1 > 0.5d && p.v2 > 0.5 && p.s.length() > 16)
                .collect(Collectors.toList());
    }
    
    public static class Price {
        double v1, v2;
        String s;
    
        public Price(Random random) {
            this.v1 = random.nextDouble();
            this.v2 = random.nextDouble();
            this.s = UUID.randomUUID().toString().substring(0,(int)(32*random.nextDouble()));
        }
    }
    }
    

    【讨论】:

    • 我通过了几乎相同的步骤进行检查。我同意你的观点,第一个更具可读性。
    • 请看看如何proper benchmark
    • @Lino 使测试更正确,但总体结果相同
    猜你喜欢
    • 2015-10-18
    • 2011-09-30
    • 2012-06-04
    • 2018-11-05
    • 1970-01-01
    • 1970-01-01
    • 2014-02-06
    • 2019-03-11
    • 2020-06-30
    相关资源
    最近更新 更多