【问题标题】:Java streams optional with null check on two itemsJava 流可选,对两个项目进行空检查
【发布时间】:2020-03-02 12:57:15
【问题描述】:

我有以下代码适用于两个值,即。 ListStringStringListString 都可以是 null

List<String> myStringList = getMyStringList(); // may return null
String myString = "abc"; // may be null

List<String> finalStringList = Optional.ofNullable(myStringList)
            .map(strings -> strings.stream()
                    .filter(obj -> StringUtils.isEmpty(myString)
                            || myString.equals(obj))
                    .collect(Collectors.toList()))
            .orElseGet(ArrayList::new);

想法是: 如果myStringListnull,则结果为空ArrayList。 如果myStringnull,则按原样返回myStringList。 如果myString 不为空,则遍历列表并返回所有匹配myString 的项目。

问题是,通过上述解决方案,即使myString 为空,我仍会遍历整个列表 (strings.stream)。

假设myStringList 很大,当myString 为空时如何避免循环列表?

也就是说,

1) 对列表进行空检查。如果为 null,则返回一个新的空列表。

2) 对myString 进行空检查。如果为 null,则按原样返回列表而不循环。

3) 如果myString 不为null,则循环遍历列表并返回过滤后的列表。

【问题讨论】:

  • 为什么你不使用简单的 if 检查。
  • 最佳解决方案:修复 getMyStringList() 以返回空列表而不是 null
  • 相关且很好读:How to best create a Java 8 stream from a nullable object?(我建议这个问题与那个问题部分重复)
  • @Vicky 如果你能用 myStringmyStringList 的存在和缺席的所有 4 种可能组合来得出期望值,那么问题就很清楚了。

标签: java java-stream optional


【解决方案1】:

就个人而言,我认为if-else 的代码将更具可读性/可维护性/高效性:

if (myStringList == null || myStringList.size() == 0) {
    return Collections.emptyList();
} else if (StringUtils.isEmpty(myString)) {
    return new ArrayList<>(myStringList); // or return myStringList;
} else {
    return myStringList.stream().filter(e -> myString.equals(e))
                                .collect(Collectors.toList());
}

好的,如果你真的不想写几行代码,试试我的库:abacus-util

return N.isNullOrEmpty(myString) ? N.newArrayList(myStringList) 
                                 : N.filter(myStringList, e -> myString.equals(e));

【讨论】:

    【解决方案2】:

    其他解决方案

    List<String> myStringList = Arrays.asList("abc", "aef"); // may return null
    String myString = "abc"; // may be null
    
    Map<Predicate, Supplier<List<String>>> predicateMap = Map.of(
          (v) -> myStringList == null, () -> new ArrayList<>(), // do a null check on the list. If null, return a new empty list.
          (v) -> myString == null, () -> myStringList, // do a null check on myString. If null, return the list as is WITHOUT LOOPING.
          (v) -> true, () -> myStringList.stream() // if myString is not null, loop through the list and return the filtered list.
                    .filter(myString::equals)
                    .collect(Collectors.toList())
          );
    List<String> outputList = predicateMap.entrySet().stream()
          .filter(p -> p.getKey().test(null))
          .findFirst().get().getValue().get();
    
    System.out.println(outputList);
    

    【讨论】:

      【解决方案3】:

      这可能对您有用(使用 Java 9+):

      List<String> result2 = Optional.ofNullable(myString)
        .filter(Objects::nonNull)
        .map(mystr -> Optional.ofNullable(myStringList)
                              .filter(Objects::nonNull)
                              .or(() -> Optional.of(new ArrayList<>()))
                              .stream()
                              .flatMap(List::stream)
                              .filter(mystr::equals)
                              .collect(Collectors.toList())) //ArrayList not guaranteed
          .orElse(myStringList);
      

      如果您使用的是 Java 8,以下内容应该可以工作,但看起来有点难以阅读:

      List<String> result = Optional.ofNullable(myString)
        .filter(Objects::nonNull)
        .map(mystr -> Optional.ofNullable(myStringList)
                      .filter(Objects::nonNull)
                      .map(list -> list.stream()
                                  .filter(mystr::equals)
                                  .collect(Collectors.toList()))
                      .orElseGet(ArrayList::new))
        .orElse(myStringList);
      

      【讨论】:

      • 我使用的是 Java 8。但是您使用 Java 8 的解决方案不起作用。
      • 当 myStringList 为空时抛出 NullPointerException。
      • @Vicky。不,我在测试时得到一个空列表。您可能需要分享您的 sn-p。
      • 在答案中添加了 sn-p
      • @Vicky 说明您的要求 * 如果myString 为空,则按原样返回myStringList*。在您的示例中,myString 为空,因此结果为空。但是您在调用System.out.println(result.size()); 时没有检查result 是否为空。这解释了你的 NPE。对吗?
      【解决方案4】:

      你可以使用三元像,

      List<String> finalStringList = myString == null ?
              Collections.emptyList()
              : Optional.ofNullable(myStringList)
              .map(strings -> strings.stream()
                      .filter(obj -> StringUtils.isEmpty(myString)
                              || myString.equals(obj))
                      .collect(Collectors.toList()))
              .orElseGet(ArrayList::new);
      

      或基本的if,默认为空列表。喜欢,

      List<String> finalStringList = Collections.emptyList();
      if (myString != null) {
          finalStringList = Optional.ofNullable(myStringList)
                  .map(strings -> strings.stream()
                          .filter(obj -> StringUtils.isEmpty(myString)
                                  || myString.equals(obj))
                          .collect(Collectors.toList()))
                  .orElseGet(ArrayList::new);
      }
      

      【讨论】:

      • 所以你认为我将不得不使用三元......我想到了......但想确定这是否是唯一的方法,并且不能使用 Streams 和 Optional 来完成......
      • if 不使用三元;所以你不必“必须使用三元”。但你可以。
      • 正如此要求所说:“2) 对 myString 进行空检查。如果为空,则按原样返回列表而不循环。”而不是这个Collections.emptyList() 应该是myStringList
      猜你喜欢
      • 2018-03-05
      • 2016-02-22
      • 1970-01-01
      • 2021-11-28
      • 1970-01-01
      • 2020-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多