Java使用"set"进行分组和映射,但如果所有值都为"null",则需要一个空集

s-h*_*ins 6 java java-stream collectors

在我的Java 11应用程序中,我想从存储库获取产品更新.一个产品更新有一个updateId和productIds要更新的列表.

  • 如果没有应该更新的产品编号以进行更新updateId = X,我仍然希望写入另一个我已处理更新的表X; updateStatusRepository.setStatusProcessing(updateId)并且updateStatusRepository.setStatusProcessed(updateId)应该仍然需要这个updateId.

  • 如果存在产品更新,则应在中进行处理ProductProcessingService.

现在,groupingBy并mapping给我一个带有null条目而不是空集的Set,这就是我之后删除所有null产品ID的原因.

List<ProductUpdate> productUpdateList = updateStatusRepository.getProductUpdates();
Map<String, Set<String>> productUpdateMap = productUpdateList
          .stream()
          .collect(
              Collectors.groupingBy(
                  ProductUpdate::getUpdateId,
                  Collectors.mapping(ProductUpdate::getProductNo, Collectors.toSet())));

productUpdateMap.forEach(
          (updateId, productIds) -> {
        try {
          updateStatusRepository.setStatusProcessing(updateId);
          productIds.remove(null);
          if(!productIds.isEmpty()) {
            productProcessingService.performProcessing(Lists.newArrayList(productIds));
          }
          updateStatusRepository.setStatusProcessed(updateId);
        } catch (Exception e) {
              //
        }
});
Run Code Online (Sandbox Code Playgroud)

我更喜欢是否可以以mapping这样的方式使用它,如果所有值都是直接传递空集null.

有没有办法优雅地做到这一点?

Fed*_*ner 10

你可以使用Collectors.filtering:

Map<String, Set<String>> productUpdateMap = productUpdateList
      .stream()
      .collect(Collectors.groupingBy(
               ProductUpdate::getVersionId,
               Collectors.mapping(ProductUpdate::getProductNo, 
                                  Collectors.filtering(Objects::nonNull, 
                                                       Collectors.toSet()))));
Run Code Online (Sandbox Code Playgroud)

我认为Collectors.filtering适合您的确切用例:它将过滤掉null产品编号,如果所有产品编号都是空的,则留空null.


编辑:请注意,在这种情况下,使用Collectors.filtering下游收集器与Stream.filter收集前使用不同.在后一种情况下,如果我们null在收集之前过滤掉带有产品编号的元素,我们最终可能会得到一个没有某些版本ID条目的地图,即如果所有产品编号都是null针对一个特定版本ID.

来自Collectors.filteringdocs:

API注意:

的filtering()在多级降低使用时,诸如下游收集器是最有用的groupingBy或partitioningBy.例如,给定一个流Employee,以累积每个部门中具有高于特定阈值的薪水的员工:

Map<Department, Set<Employee>> wellPaidEmployeesByDepartment
  = employees.stream().collect(
    groupingBy(Employee::getDepartment,
               filtering(e -> e.getSalary() > 2000,
                         toSet())));
Run Code Online (Sandbox Code Playgroud)

过滤收集器与流的filter()操作不同.在此示例中,假设在某些部门中没有员工的薪水高于阈值.使用如上所示的过滤收集器将导致从该部门到空的映射Set.如果filter()改为完成了流操作,那么根本就没有该部门的映射.


编辑2:我认为值得一提的是@Holger在评论中提出的备选方案:

Map<String, Set<String>> productUpdateMap = productUpdateList
      .stream()
      .collect(Collectors.groupingBy(
               ProductUpdate::getVersionId, 
               Collectors.flatMapping(pu -> Stream.ofNullable(pu.getProductNo()), 
                                      Collectors.toSet())));
Run Code Online (Sandbox Code Playgroud)

  • 或者`productUpdateList .stream().collect(Collectors.groupingBy(ProductUpdate :: getVersionId,Collectors.flatMapping(pu-> Stream.ofNullable(pu.getProductNo()),Collectors.toSet())))` (6认同)