2017-05-27 66 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中有一個令人難以置信的成就,以及一個打印出「你的總成就是......」或「你還沒玩過這個遊戲,試用它!」的應用程序,這樣的事情: - ) –