2017-05-27 65 views
5

近年来,随着Java的8流工作时,我碰到一个NullPointerException上降低操作来与下面的测试案例工作时:NPE的Java流减少操作

private static final BinaryOperator<Integer> sum = (a, b) -> { 
    if (a == null) return b; 
    if (b == null) return a; 
    return Integer.sum(a, b); 
}; 

List<Integer> s = new ArrayList<>(); 
s.add(null); 
s.add(null); 
s.add(null); 

Integer i = s.stream().reduce(sum).orElse(null); 
// throws NPE 

Integer i = s.stream().reduce(sum).orElse(2); 
// throws NPE 

Integer i = s.stream().reduce(null,(a, b)->null); 
// returns a value i.e null 

或者:

Integer i = s.stream().filter(Objects::nonNull).reduce(Integer::sum).orElse(null); 
// returns a value i.e null 

在检查减少操作时,我遇到了执行减少操作的类:

class ReducingSink implements AccumulatingSink<T, Optional<T>, ReducingSink> { 
    private boolean empty; 
    private T state; 

    public void begin(long size) { 
     empty = true; 
     state = null; 
    } 

    @Override 
    public void accept(T t) { 
     if (empty) { 
      empty = false; 
      state = t; 
     } else { 
      state = operator.apply(state, t); 
     } 
    } 

    @Override 
    public Optional<T> get() { 
     return empty ? Optional.empty() : Optional.of(state); 
    } 

    @Override 
    public void combine(ReducingSink other) { 
     if (!other.empty) 
      accept(other.state); 
    } 
} 

在上面的代码中,如果布尔型empty为假,则看到get()方法返回可选值,在我的情况下,该值为false,但state为空,因此Optional.of(null)将抛出NullPointerException。在我的情况下,我有一个二进制运算符,它允许null

所以我觉得代码

return empty ? Optional.empty() : Optional.of(state); 

应改为

return empty || state == null ? Optional.empty() : Optional.of(state); 

正如我的二进制运算符(其具有减少的任务),并且是好的与null

+3

'Optional.ofNullable'如何?无论如何,你的例子看起来不正确,因为你在'BinaryOperator '中使用'List '。 – Pshemo

+2

@Pshemo'ReducingSink'是一个内部jdk类 – Eugene

+0

反正 - @ Pshemo是对的 - 这个例子编译得不好:-) –

回答

4

我真的不能告诉你为什么有零点工作,这似乎是一个坏主意一个开始。而且,正如你所见,你不能使用空作为输入reduce。你可以建立自己的自定义Collector(你不能建立你自己的Reducer)。

您采取了哪些:

Double result = s.stream() 
     .filter(Objects::nonNull) 
     .reduce(Double::sum) 
     .orElse(null); 

完美的罚款 BTW。获得空结果的唯一方法是当您的输入中的全部元素为空,因此最初过滤它们是最好的选择。对于它的乐趣,我决定写一个自定义的收集器(真的不能告诉为什么,认为这将是有趣的我猜的)

Double result = s.stream() 
      .parallel() 
      .collect(
       () -> new Double[] { null }, 
       (left, right) -> { 
        if (right != null) { 
         if (left[0] != null) { 
          left[0] = right + left[0]; 
         } else { 
          left[0] = right; 
         } 
        } 
       }, 
       (left, right) -> { 
        if (right[0] != null) { 
         if (left[0] != null) { 
          left[0] = right[0] + left[0]; 
         } else { 
          left[0] = right[0]; 
         } 
       }})[0]; 

如果需要,您可以把它变成类本身:

class NullableCollector implements Collector<Double, Double[], Double> { 

    @Override 
    public BiConsumer<Double[], Double> accumulator() { 
     return (left, right) -> { 
      if (right != null) { 
       if (left[0] != null) { 
        left[0] = right + left[0]; 
       } else { 
        left[0] = right; 
       } 
      } 
     }; 
    } 

    @Override 
    public Set<Characteristics> characteristics() { 
     return EnumSet.noneOf(Characteristics.class); 
    } 

    @Override 
    public BinaryOperator<Double[]> combiner() { 
     return (left, right) -> { 
      if (right[0] != null) { 
       if (left[0] != null) { 
        left[0] = right[0] + left[0]; 
       } else { 
        left[0] = right[0]; 
       } 
      } 
      return left; 
     }; 
    } 

    @Override 
    public Function<Double[], Double> finisher() { 
     return (array) -> array[0]; 
    } 

    @Override 
    public Supplier<Double[]> supplier() { 
     return() -> new Double[] { null }; 
    } 

} 
7

documentation的减少你使用状态操作:

抛出: NullPointerException - 如果减少的结果为空

因此,即使您的二元运算符使用null,您看到的NPE也会被记录并预期结果。

的文档更详细的给予额外的洞察力与一些等效代码:

 boolean foundAny = false; 
    T result = null; 
    for (T element : this stream) { 
     if (!foundAny) { 
      foundAny = true; 
      result = element; 
     } 
     else 
      result = accumulator.apply(result, element); 
    } 
    return foundAny ? Optional.of(result) : Optional.empty(); 

NPE是在这种情况下的最后一行抛出。

如果您在库中应用了您的建议更改,我们将无法区分从还原结果为null的流中减少空流的结果。

+0

我同意你的意见指出,如果应用了建议的更改,那么我们将无法区分从还原结果为null的流中减少空流的结果,但是可以为其建议一个用例。 –

+0

呃......在游戏X中有一个令人难以置信的成就,以及一个打印出“你的总成就是......”或“你还没玩过这个游戏,试用它!”的应用程序,这样的事情: - ) –