【问题标题】:Using Stream API to count stats使用 Stream API 统计数据
【发布时间】:2021-09-14 08:12:09
【问题描述】:

我有以下代表小吃店的模型 SnackBarStat - 这有预订的开始日期、预订中的客户数量和天数(客户可以设置开始日期并说出他们想要多少天的餐桌)

public class SnackBarStat {
  LocalDate startDate;
  int numberOfDays;
  int customers;
}

现在给出一个这样的统计数据列表,我试图找出每个日期小吃店里有多少顾客

例如,如果输入是

Start date 13-9-2021, Customers: 2 , numberOfDays: 3
Start date 12-9-2021, Customers: 3 , numberOfDays: 2
Start date 13-9-2021, Customers: 1 , numberOfDays: 1

预期输出为

{2021-09-12=3, 2021-09-13=6, 2021-09-14=2, 2021-09-15=2}

到目前为止我已经尝试过。 我创建了一个简单的逻辑来迭代每个 startDate,根据 numberOfDays 扩展日期,然后为每个日期将它们添加到地图中,并在该日期汇总客户

public class SnackBarOccupanceTest {
  public static void main(String args[]){
    SnackBarStat snackBarStat1 = new SnackBarStat();
    snackBarStat1.setStartDate(LocalDate.of(2021, 9, 13));
    snackBarStat1.setCustomers(2);
    snackBarStat1.setNumberOfDays(3);

    SnackBarStat snackBarStat2 = new SnackBarStat();
    snackBarStat2.setStartDate(LocalDate.of(2021, 9, 12));
    snackBarStat2.setCustomers(3);
    snackBarStat2.setNumberOfDays(2);

    SnackBarStat snackBarStat3 = new SnackBarStat();
    snackBarStat3.setStartDate(LocalDate.of(2021, 9, 13));
    snackBarStat3.setCustomers(1);
    snackBarStat3.setNumberOfDays(1);

    Map<LocalDate, Integer> occupancePerDate = new HashMap<>();
    List<SnackBarStat> snackBarStats = List.of(snackBarStat1, snackBarStat2, snackBarStat3);
    for(SnackBarStat snackBarStat:snackBarStats){
      var expandedDates = expandDates(snackBarStat.getStartDate(), snackBarStat.getNumberOfDays());
      for(LocalDate eachDate : expandedDates){
        if(occupancePerDate.get(eachDate) != null){
          var existingCustomers = occupancePerDate.get(eachDate);
          occupancePerDate.put(eachDate, existingCustomers + snackBarStat.getCustomers());
        }else{
          occupancePerDate.put(eachDate, snackBarStat.getCustomers());
        }
      }
    }
    System.out.println(occupancePerDate);
  }

  public static List<LocalDate> expandDates (LocalDate startDate, int numberOfDays){
    List<LocalDate> dateRanges = new ArrayList<>();
    for(int i = 0; i < numberOfDays; i++){
      dateRanges.add(startDate.plusDays(i));
    }
    return dateRanges;
  }
}

此代码有效。我想知道流 api 是否有更短的方法来做同样的事情。 请注意,代码仅用于演示目的,我在实际代码中使用了更好的代码实践。

【问题讨论】:

  • 您可以使用occupancePerDate.getOrDefault,任何可能只是内联expandDates(无需创建该临时列表),否则我将保持原样。
  • 不是关于 Stream API 而是关于性能:如果你有 N 个统计数据,每个统计数据都有相同的开始日期,并且 numberOfDays = N,那么它的复杂性将是 O(N²)。相反,如果您使用 TreeMap 并将 +customers 添加到 startDate-customersstartDate + numberOfDays 您可以将其降低到 O(N) 或 O(NlogN)。不过,实现有点复杂,所以只有在统计数据之间存在显着重叠时才值得。

标签: java java-8 java-stream


【解决方案1】:

您可以使用flatMap 来“展开”日期,然后您可以使用groupingBy 来对展开的日期进行分组:

Map<LocalDate, Integer> occupancePerDate = snackBarStats.stream()
    .flatMap(x -> // expand the dates
        IntStream.range(0, x.getNumberOfDays())
            // for each date, we create a new SnackBarStat with 
            // that date, numberOfDays=1, and the same number of customers
            .mapToObj(n -> new SnackBarStat(x.getStartDate().plusDays(n), 1, x.getCustomers()))
    ).collect(Collectors.groupingBy(
        SnackBarStat::getStartDate, // group by start date
        Collectors.summingInt(SnackBarStat::getCustomers) // for each group, sum the customers
    ));

虽然它更短,但请注意它可能比您的循环慢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-13
    • 2017-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多