对流中的 Instanceof 应用模式匹配

Dar*_*iva 3 java instanceof java-stream

假设我们有一个动物流。

我们有不同的动物子类,并且我们希望在流上应用过滤器以仅包含流中的斑马。我们现在仍然有动物流,但只包含斑马。为了获得斑马流,我们仍然需要进行投射。

Stream<Zebra> zebraStream = animalStream
    .filter(Zebra.class::isInstance)
    .map(Zebra.class::cast);
Run Code Online (Sandbox Code Playgroud)

Java 14 引入了instanceof 的模式匹配,所以我们现在可以使用:

if (animal instanceof Zebra zebra) {
    System.out.println(zebra.countStripes());
}
Run Code Online (Sandbox Code Playgroud)

有没有办法在流管道中使用模式匹配?当然你可以这样做:

Stream<Zebra> zebraStream = animalStream.map(animal -> {
        if (animal instanceof Zebra zebra) {
            return zebra;
        }
        return null;
    })
    .filter(Objects::nonNull);
Run Code Online (Sandbox Code Playgroud)

但恕我直言,这真的很难看。

Ale*_*nko 5

模式匹配+mapMulti

要将超类型的 Stream 强制为其子类型之一的 Stream,您可以将instanceof 的模式匹配与 Java 16 结合使用mapMulti(),它需要一个流元素和一个Consumer结果类型:

Stream<Animal> animalStream = Stream.of();

Stream<Zebra> zebraStream = animalStream
    .mapMulti((animal, consumer) -> {
        if (animal instanceof Zebra zebra) consumer.accept(zebra);
    });
Run Code Online (Sandbox Code Playgroud)

模式匹配 + flatMap

要对 instanceof 使用模式匹配,您还可以使用经典的流操作flatMap(),这就像mapMulti()执行一对多转换。

两者之间的重要区别在于,通过其mapMulti()将初始流元素替换为零个或多个Consumer元素,同时flatMap()需要生成一个新的流来压平数据。在这种情况下,利用mapMulti()会更有优势,因为如果列表很大,为每个元素生成单例流可能会很昂贵。

Stream<Zebra> zebraStream = animalStream
    .flatMap(animal -> 
        animal instanceof Zebra zebra ? Stream.of(zebra) : null
    );
Run Code Online (Sandbox Code Playgroud)

请注意,根据文档,我们也可以返回而不是空流null这很方便,因为 Stream.empty() 不返回常量,而是生成一个新对象):

如果映射流是null空流,则使用空流。

  • 我仍然希望有一天,流能够足够智能地自动执行此操作 (2认同)