Java 是否履行了“义务”;过滤然后映射“;及;映射然后过滤";在一条小溪里有什么不同?

Java 是否履行了“义务”;过滤然后映射“;及;映射然后过滤";在一条小溪里有什么不同?,java,java-8,java-stream,Java,Java 8,Java Stream,我想知道什么更快:按字段过滤自定义对象,然后按字段映射,反之亦然(映射,然后过滤)。 最后,我通常希望将映射字段收集到某个集合中 例如,最简单的Person类: public class Person { String uuid; String name; String secondName; } 现在让我们有一个人员列表 List<String> filtered1 = persons .stream()

我想知道什么更快:按字段过滤自定义对象,然后按字段映射,反之亦然(映射,然后过滤)。
最后,我通常希望将映射字段收集到某个集合中

例如,最简单的Person类:

public class Person {
    String uuid;
    String name;
    String secondName;
}
现在让我们有一个
人员列表

List<String> filtered1 = persons
                .stream()
                .filter(p -> "NEED_TOY".equals(p.getName()))
                .map(Person::getName)
                .collect(Collectors.toList());
// or?
List<String> filtered2 = persons
                .stream()
                .map(Person::getName)
                .filter(p -> "NEED_TOY".equals(p))
                .collect(Collectors.toList());
List filtered1=个人
.stream()
.filter(p->“NEED_TOY”.equals(p.getName()))
.map(Person::getName)
.collect(Collectors.toList());
//还是?
列表过滤器2=人
.stream()
.map(Person::getName)
.filter(p->“需要玩具”。等于(p))
.collect(Collectors.toList());

在这个特定示例中,调用
Person.getName()
基本上没有任何成本,这无关紧要,您应该使用您认为最可读的内容(之后的过滤甚至可能会稍微快一点,因为正如TJ提到的,映射操作是过滤操作的一部分)

但是,如果映射操作的成本很大,那么首先过滤(如果可能)会更有效,因为流不必映射已过滤掉的元素

让我们举一个人为的例子:您有一个ID流,对于流中的每个偶数ID,您必须执行http GET请求或数据库查询,以获取由该ID标识的项的详细信息(从而将该ID映射到详细对象)


假设流由半偶数和半奇数ID组成,并且每个请求占用相同的时间,您可以通过先过滤将时间除以2。如果每个http请求需要1秒的时间,并且您有60个ID,那么对于同一个任务,您可以先进行过滤,从60秒增加到30秒,还可以减少网络和外部http API的费用。

显然,性能完全取决于

  • 流式传输时执行的复杂操作(业务逻辑)
  • 您的数据有多复杂
让我们看两个简单的场景

情景1 如果map函数需要执行一些复杂的操作,例如调用一些外部restapi来操作流对象,那么在这种情况下,我建议在map之前先进行过滤,因为这样可以减少不必要的昂贵REST调用。 在这种方法中,当我们首先进行过滤时,显然它会对所有匹配对象执行两次映射操作

情景2 假设您需要首先基于一些外部RESTAPI调用或函数操作数据流,然后根据这些结果进行过滤。显然,在这个场景中,您需要在过滤流之前首先映射。 由于映射操作是过滤操作的一部分,因此与前一种方法相比,这种方法可以稍微快一些


您是否尝试过分析这两种方法?只需使用
System.nanoTime()
获取执行前后的时间,并记录差异。我不确定JVM的优化流有多“聪明”,但我猜,
filtered1
稍微快一点,因为它必须通过比原来更小的数组进行映射。@auskenfuchs-流不是这样工作的。但对于流,过滤可以减少映射调用,这仍然是事实,它只是不能真正产生一个过滤后的东西,然后再次通过它来映射。但是,正如您所说,分析实际代码(如果有一些性能问题必须解决)确实是正确的做法。您没有注意到由
map
执行的相同操作也由
过滤器执行。因此,首先执行
filter
是对所有匹配对象执行两次映射操作。不仅在OP的示例中是filter谓词的映射操作部分。这适用于这个问题有意义的所有场景。当谓词对映射操作的结果进行操作时,可以直接首先执行映射操作,这样可读性更高,减少了代码重复并避免了冗余工作。但是,当过滤器不对映射函数的结果进行操作时,就不会出现将其放置在何处的问题,因为所需的输入要求进行放置,并且您无论如何都无法交换它们。