【问题标题】:Can Java's Stream.collect() return null?Java 的 Stream.collect() 可以返回 null 吗?
【发布时间】:2018-05-09 15:12:57
【问题描述】:

Stream.collect() 的 JavaDoc 说它返回“减少的结果”。这并不能告诉我这样的代码是否可以为 filteredList 返回 null:

List<String> filteredList = inputList.stream()
    .filter(c -> c.isActive())
    .collect(Collectors.toList());

我希望如果它可以返回 null,那么它会返回一个 Optional,但它也没有这么说。

Stream.collect() 是否可以返回 null 是否记录在任何地方?

【问题讨论】:

  • 不,即使没有过滤任何内容,这也会返回一个空列表

标签: java java-stream


【解决方案1】:

Collector.toList() 将为您返回一个列表。

这里是实现:

public static <T>
Collector<T, ?, List<T>> toList() {
    return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_ID);
}

如您所见,ArrayList::new 被用作物品的容器。

来自Collector的JavaDoc:

可变归约运算 将输入元素累积到可变结果容器中,可选 之后将累积的结果转换为最终表示 所有输入元素都已处理。减少操作可以 顺序或并行执行。

收集器由四个共同作用的函数指定 将条目累积到可变结果容器中,并且可以选择 对结果执行最终转换。它们是:

  • 创建新的结果容器(supplier())

  • 将新数据元素合并到结果容器中(accumulator())

  • 将两个结果容器合并为一个(combiner())
  • 对容器执行可选的最终转换 (finisher())

使用收集器的减少的顺序实现将 使用供应商函数创建单个结果容器,并且 为每个输入元素调用一次累加器函数。一种 并行实现会对输入进行分区,创建结果 每个分区的容器,累加每个分区的内容 分区为该分区的子结果,然后使用 combiner 函数将子结果合并成一个组合结果。

只要你不做奇怪的事情,比如组合函数返回nullCollector 总是使用你提供的supplier 函数返回至少一个mutable container

而且我认为如果一个实现会返回 null 容器,这是非常违反直觉的。

【讨论】:

    【解决方案2】:

    这不取决于Stream.collect,而是取决于个人CollectorCollectors.toList() 将返回一个空的 ArrayList

    也就是说,在某些情况下,没有理由不能使用奇怪的 Collector 来返回 null:

    .collect(
        Collector.of(
            ArrayList::new,
            ArrayList::add,
            (a, b) -> {
                a.addAll(b);
                return a;
            },
            a -> a.isEmpty() ? null : a  // finisher replaces empty list with null
        )
    );
    

    所以Collector 是您需要记住检查的内容。我相信所有Collectors available out-of-the-box 都会返回空集合,正如您所期望的那样。

    【讨论】:

    • 我相信这正确的答案,现在在电话上,但真的没有关于这个的规范吗?我会怀疑...
    • 同意尤金,这应该是正确的答案
    【解决方案3】:

    您可以使用Collectors::collectingAndThen 将collect() 结果传递给Function&lt;T,R&gt;Function&lt;T,R&gt; 的返回值将是 collect() 的返回值。

    List<String> filteredList = inputList.stream()
     .filter(c -> c.isActive())
     .collect(Collectors.collectingAndThen(Collectors.toList(), c -> !c.isEmpty()?c:null));
    

    【讨论】:

      【解决方案4】:

      我认为这部分文档说它不能为空:

      返回一个收集器,将输入元素累积到一个new 列表

      亮点由我添加。我认为这个新列表意味着不为空的东西。

      我开始检查ReferencePipeline.collect() 以检查实际实现是否正确。不幸的是,这是一次徒劳的尝试。这里的案例太多了,好像是平行的?是在forEach 之后吗?等等

      【讨论】:

      • 非常薄的 IMO...
      • 在我看来,javadoc 应该是presize 并且切中要害。它清楚地说明了您对“新列表”方法的期望。它不应提及此方法“不”返回的其他值。
      【解决方案5】:

      这取决于收集器。您正在使用的 (Collectors.toList()) 返回一个空列表。

      【讨论】:

        【解决方案6】:

        没有收集永远不会返回null,为了检查使用isEmpty()而不是null

        【讨论】:

          【解决方案7】:

          如果您确实需要在列表为空时返回null(假设变量c的类型为MyObj),我相信以下代码是一个很好的实现:

          import static java.util.stream.Collectors.collectingAndThen;
          import static java.util.stream.Collectors.toList;
          
          ...
          
            List<String> filteredList = inputList.stream()
              .filter(MyObj::isActive)
              .collect(collectingAndThen(toList(), Stream::of)
              .filter(List::isEmpty)
              .findAny()
              .orElse(null);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-06-24
            • 2011-12-10
            • 2012-07-06
            • 2016-10-17
            • 2010-11-10
            • 2023-03-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多