代码之家  ›  专栏  ›  技术社区  ›  Lulex97

如何用流替换iter循环

  •  0
  • Lulex97  · 技术社区  · 2 年前

    我正在尝试使用流替换那个代码。

    public int countPenaltyPointsTest(Set<OffenceEntity> offences, LocalDate date) {
        int sum = 0;
        for (OffenceEntity offence : offences) {
            for (OffenceDetailsEntity offenceDetailsEntity : offence.getDetails()) {
                if (date.isAfter(offenceDetailsEntity.getStartDate())
                        && date.isBefore(offenceDetailsEntity.getEndDate())) {
                    sum +=offenceDetailsEntity.getPenaltyPoints();
                }
            }
        }
    

    我有一对多关系的犯罪实体,有犯罪细节实体:

    @Entity
    public class OffenceEntity {
        ...
        private Set<OffenceDetailsEntity> details;
    }
    
    
    @Entity
    public class OffenceDetailsEntity {
        ...
        private int penaltyPoints;
        private LocalDate startDate;
        private LocalDate endDate;
        @ManyToOne
        @JoinColumn(referencedColumnName = "id")
        private OffenceEntity offence;
    }
    

    如果日期介于两个日期之间,我想收集罚分。 我知道如何做到这一点,例如,当我在犯罪实体中有类似场地的罚分时。 实例

    int sum = offences.stream()
                .mapToInt(OffenceEntity::getPenaltyPoints)
                .sum();
    

    但我不知道如何“跳”到《犯罪实体》的片场

    1 回复  |  直到 2 年前
        1
  •  3
  •   Alexander Ivanchenko    2 年前

    由于您需要压平流数据,即翻转类型的每个元素 OffenceEntity 分成一组元件, mapToInt() 不是正确的操作。为此,您可以使用 flatMap() (或它的味道 flatMapToInt() ).

    区分两种操作 map() flatMap() ,记住一个简单的原则:

    • 当你需要的时候 一对一 转型 使用 地图 或者是味道
    • 对于 一对多 转型 - flatMap() 或者是味道。

    由于Java 16,我们还可以利用 mapMulty() 以使流中的数据变平。但它是一种特殊用途的工具,而不是一种常见的选择。除了平坦化之外,此操作还允许过滤掉流元素,因此与 flatMap() 它将一个元素变成 1+ ( 一个或多个 )元素, mapMulty() 生产 0+ ( 零或更多 )元素。

    flatMap()

    flatMap() 期望函数作为参数,该函数接受元素并生成流( 这也是 map flatMap ).

    流数据扁平化后,我们需要进行过滤 OffenceDetailsEntity 具有合适日期和摘录的对象 penaltyPoints 。可以使用 filter() + mapToInt() .

    public int countPenaltyPointsTest(Set<OffenceEntity> offences, LocalDate date) {
        
        return offences.stream()                                // Stream<OffenceEntity>
            .flatMap(offence -> offence.getDetails().stream())  // Stream<OffenceDetailsEntity>
            .filter(ode -> date.isAfter(ode.getStartDate())     // Stream<OffenceDetailsEntity>
                            && date.isBefore(ode.getEndDate()))
            .mapToInt(OffenceDetailsEntity::getPenaltyPoints)   // IntStream
            .sum();
    }
    

    mapMulty()

    此操作允许结合命令式编程功能( 即循环和条件语句 )进入流管道。

    如前所述,这是一个 专用工具 有很多特殊之处,应该用心去运用。

    这是引用的 API Note 规定何时使用 mapMulty() :

    此方法优于 flatMap 在以下情况下:

    • 将每个流元素替换为 小的 ( 可能为零 )元素的数量。使用此方法可以避免创建 一个新的 Stream 根据需要,用于每组结果元素的实例 通过 flatMap .
    • 当它是 更易于使用 命令式方法 用于生成结果元素,而不是以 流动 .

    例如, mapMulty() 能够替代组合 flatMap() + 滤器 ,或多个 flatMap() 操作。

    它需要类型为的参数 BiConsumer ,即消费者,反过来 两个论点 : 流元素 和一个 消费者 结果类型的。提供给消费者的每个值都将成为一个新的流元素,取代初始元素。

    以下是如何使用实现此方法 mapMultyToInt() :

    public int countPenaltyPointsTest(Set<OffenceEntity> offences, LocalDate date) {
        
        return offences.stream()
            .mapMultiToInt((offence, consumer) -> 
                offence.getDetails().forEach(ode -> {
                    if (date.isAfter(ode.getStartDate()) && date.isBefore(ode.getEndDate()))
                        consumer.accept(ode.getPenaltyPoints());
            }))
            .sum();
    }
    

    有关如何使用的更多示例和信息 mapMulty() ,看看 this question .