【问题标题】:Check instanceof in stream检查流中的 instanceof
【发布时间】:2016-06-14 21:52:57
【问题描述】:

我有以下表达:

scheduleIntervalContainers.stream()
        .filter(sic -> ((ScheduleIntervalContainer) sic).getStartTime() != ((ScheduleIntervalContainer)sic).getEndTime())
        .collect(Collectors.toList());

...其中scheduleIntervalContainers 的元素类型为ScheduleContainer

final List<ScheduleContainer> scheduleIntervalContainers

是否可以在过滤器之前检查类型?

【问题讨论】:

    标签: java java-8 java-stream instanceof


    【解决方案1】:

    您可以应用另一个filter 以仅保留ScheduleIntervalContainer 实例,添加map 将节省您以后的演员表:

    scheduleIntervalContainers.stream()
        .filter(sc -> sc instanceof ScheduleIntervalContainer)
        .map (sc -> (ScheduleIntervalContainer) sc)
        .filter(sic -> sic.getStartTime() != sic.getEndTime())
        .collect(Collectors.toList());
    

    或者,正如 Holger 所评论的,如果您更喜欢这种风格,您可以将 lambda 表达式替换为方法引用:

    scheduleIntervalContainers.stream()
        .filter(ScheduleIntervalContainer.class::isInstance)
        .map (ScheduleIntervalContainer.class::cast)
        .filter(sic -> sic.getStartTime() != sic.getEndTime())
        .collect(Collectors.toList());
    

    【讨论】:

    • 或者.filter(ScheduleIntervalContainer.class::isInstance) .map(ScheduleIntervalContainer.class::cast),随便你喜欢什么风格。
    • 在IDEA和Java8中,如果将上面的sn-p赋值给List scheduleIntervalContainers,它仍然提示我将结果显式地转换为List scheduleIntervalContainers,你知道为什么吗?
    • @K.Symbol 您是否尝试分配给List&lt;ScheduleContainer&gt;List&lt;ScheduleIntervalContainer&gt;?应该是后者。
    【解决方案2】:

    一个非常优雅的选择是使用类的方法引用:

    scheduleIntervalContainers
      .stream()
      .filter( ScheduleIntervalContainer.class::isInstance )
      .map( ScheduleIntervalContainer.class::cast )
      .filter( sic -> sic.getStartTime() != sic.getEndTime())
      .collect(Collectors.toList() );
    

    【讨论】:

    • 与使用 instanceof 和 (ScheduleIntervalContainer) 进行投射相比,这种风格有什么好处?
    • @MageWind 这主要是风格问题。有些人更喜欢它,因为您不必引入另一个变量名(对于 lambda 参数),其他人则因为它生成的字节码略少(虽然没有足够的差异来真正相关)。
    • 这真的很酷!但是为什么需要.classisInstance 不是 Object 的一部分吗? Class 是 Java 中的一个类吗?
    • @PostSelf 确实如此,而ScheduleIntervalContainer 不会是一个实例。
    【解决方案3】:

    @Eran 解决方案有一个小问题——在filtermap 中都输入类名很容易出错——很容易忘记在这两个地方更改类名。一个改进的解决方案是这样的:

    private static <T, R> Function<T, Stream<R>> select(Class<R> clazz) {
        return e -> clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null;
    }
    
    scheduleIntervalContainers
      .stream()
      .flatMap(select(ScheduleIntervalContainer.class))
      .filter( sic -> sic.getStartTime() != sic.getEndTime())
      .collect(Collectors.toList());   
    

    但是,为每个匹配元素创建 Stream 可能会降低性能。小心在庞大的数据集上使用它。我从@Tagir Vailev 学到了这个解决方案

    【讨论】:

    • 在这种方法中,您必须注意 NullPointerExceptions,因为 select(A.class) 将返回 null 用于任何不是 A 的东西。添加.filter(Objects::nonNull) 会有所帮助。顺便说一句:@Eran 的方法是空安全的。
    • 对不起,我的错...flatMap 的 JavaDoc 说“如果映射流为空,则使用空流。”。所以你的解决方案是正确的,即使没有空过滤器。
    • 仍然 (IMO) 很奇怪返回 null 当你本可以返回一个空流时
    【解决方案4】:

    我会推荐这种实用方法,而不是像其他答案建议的过滤器 + 地图:

    public static <Super, Sub extends Super> Function<Super, Stream<Sub>> filterType(Class<Sub> clz) {
      return obj -> clz.isInstance(obj) ? Stream.of(clz.cast(obj)) : Stream.empty();
    }
    

    将其用作:

    Stream.of(dog, cat fish)
      .flatMap(filterType(Dog.class));
    

    相比filter+map有以下优点:

    • 如果类没有扩展你的类,你会得到一个编译错误
    • 一个地方,您永远不会忘记更改过滤器或地图中的类

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-21
      • 1970-01-01
      • 2018-05-30
      相关资源
      最近更新 更多