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

如果findFirst()找到的第一个元素为空,为什么会抛出NullPointerException?

  •  119
  • neverendingqs  · 技术社区  · 9 年前

    为什么这会引发 java.lang.NullPointerException ?

    List<String> strings = new ArrayList<>();
    strings.add(null);
    strings.add("test");
    
    String firstString = strings.stream()
            .findFirst()      // Exception thrown here
            .orElse("StringWhenListIsEmpty");
            //.orElse(null);  // Changing the `orElse()` to avoid ambiguity
    

    中的第一项 strings null ,这是完全可接受的值。此外 findFirst() 返回一个 Optional ,这对 findFirst() 能够处理 无效的 s

    EDIT:更新了 orElse() 以减少歧义。

    5 回复  |  直到 3 年前
        1
  •  90
  •   Community basarat    4 年前

    其原因是使用 Optional<T> 在回报中。可选不允许包含 null 本质上,它无法区分“它不在”和“它在那里,但它被设置为 无效的 ".

    这就是为什么 the documentation 明确禁止以下情况 无效的 在中选择 findFirst() :

    投掷:

    NullPointerException -如果所选元素为 无效的

        2
  •  69
  •   Community basarat    7 年前

    already discussed ,API设计者并不认为开发人员想要 null 值和不存在的值。

    如果您仍然想这样做,可以通过应用序列显式地执行

    .map(Optional::ofNullable).findFirst().flatMap(Function.identity())
    

    流。如果没有第一个元素或者第一个元素是 无效的 。所以在您的情况下,您可以使用

    String firstString = strings.stream()
        .map(Optional::ofNullable).findFirst().flatMap(Function.identity())
        .orElse(null);
    

    获得 无效的 如果第一个元素不存在或 无效的 .

    如果要区分这些情况,可以简单地省略 flatMap 步骤:

    Optional<String> firstString = strings.stream()
        .map(Optional::ofNullable).findFirst().orElse(null);
    System.out.println(firstString==null? "no such element":
                       firstString.orElse("first element is null"));
    

    这与您更新的问题没有太大不同。你只需要更换 "no such element" 具有 "StringWhenListIsEmpty" "first element is null" 具有 无效的 。但如果你不喜欢条件句,你也可以通过以下方式实现:

    String firstString = strings.stream().skip(0)
        .map(Optional::ofNullable).findFirst()
        .orElseGet(()->Optional.of("StringWhenListIsEmpty"))
        .orElse(null);
    

    现在 firstString 无效的 如果元素存在但 无效的 这将是 “字符串列表为空” 当不存在元素时。

        3
  •  38
  •   Nathan    6 年前

    您可以使用 java.util.Objects.nonNull 在查找之前筛选列表

    类似的东西

    list.stream().filter(Objects::nonNull).findFirst();
    
        4
  •  19
  •   Nathan    8 年前

    以下代码替换 findFirst() 具有 limit(1) 并替换 orElse() 具有 reduce() :

    String firstString = strings.
       stream().
       limit(1).
       reduce("StringWhenListIsEmpty", (first, second) -> second);
    

    limit() 仅允许1个元素到达 reduce 这个 BinaryOperator 传递给 减少 返回1个元素或else "StringWhenListIsEmpty" 如果没有元素到达 减少 .

    这个解决方案的优点在于 Optional 未分配 二进制运算符 lambda不会分配任何内容。

        5
  •  1
  •   ZhongYu    9 年前

    可选应为“value”类型。(请阅读 javadoc :)JVM甚至可以替换所有 Optional<Foo> 用just Foo ,除去所有装箱和拆箱成本。A. null Foo表示空 可选<Foo> .

    这是一种可能的设计,允许空值可选,而不添加布尔标志-只需添加一个sentinel对象。(甚至可以使用 this 作为哨兵;参见Throwable.crease)

    Optional无法包装null的决定不基于运行时成本。这是一个激烈争论的问题,你需要挖掘邮件列表。这个决定并不能让每个人都信服。

    在任何情况下,由于Optional不能包装空值,因此在像这样的情况下,它会将我们推到一个角落 findFirst 。他们必须推断出空值非常罕见(甚至认为Stream应该禁止空值),因此在空值上引发异常比在空流上引发异常更方便。

    解决方法是框 无效的 ,例如。

    class Box<T>
        static Box<T> of(T value){ .. }
    
    Optional<Box<String>> first = stream.map(Box::of).findFirst();
    

    (他们说,解决每个OOP问题的方法是引入另一种类型:)